2 * Copyright (c) 1999-2003 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)
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
20 #ident "$Id: sys_vcd.c,v 1.58 2007/03/14 04:05:51 steve Exp $"
23 # include "sys_priv.h"
26 * This file contains the implementations of the VCD related
30 # include "vpi_user.h"
39 # include "vcd_priv.h"
41 static char*dump_path
= 0;
42 static FILE*dump_file
= 0;
44 static const char*units_names
[] = {
56 struct t_vpi_time time
;
58 struct vcd_info
* next
;
59 struct vcd_info
* dmp_next
;
64 static char vcdid
[8] = "!";
66 static void gen_new_vcd_id(void)
68 static unsigned value
= 0;
72 for (i
=0; i
< sizeof(vcdid
)-1; i
++) {
73 vcdid
[i
] = (char)((v
%94)+33); /* for range 33..126 */
82 static struct vcd_info
*vcd_list
= 0;
83 static struct vcd_info
*vcd_dmp_list
= 0;
84 PLI_UINT64 vcd_cur_time
= 0;
85 static int dump_is_off
= 0;
86 static long dump_limit
= 0;
87 static int dump_is_full
= 0;
88 static int finish_status
= 0;
90 static char *truncate_bitvec(char *s
)
105 return(((l
=='0')&&(r
=='1'))?s
:s
-1);
110 static void show_this_item(struct vcd_info
*info
)
114 if (vpi_get(vpiType
, info
->item
) == vpiRealVar
) {
115 value
.format
= vpiRealVal
;
116 vpi_get_value(info
->item
, &value
);
117 fprintf(dump_file
, "r%.16g %s\n", value
.value
.real
, info
->ident
);
119 } else if (vpi_get(vpiSize
, info
->item
) == 1) {
120 value
.format
= vpiBinStrVal
;
121 vpi_get_value(info
->item
, &value
);
122 fprintf(dump_file
, "%s%s\n", value
.value
.str
, info
->ident
);
124 value
.format
= vpiBinStrVal
;
125 vpi_get_value(info
->item
, &value
);
126 fprintf(dump_file
, "b%s %s\n",
127 truncate_bitvec(value
.value
.str
),
133 static void show_this_item_x(struct vcd_info
*info
)
135 if (vpi_get(vpiType
, info
->item
) == vpiRealVar
) {
136 /* Some tools dump nothing here...? */
137 fprintf(dump_file
, "rNaN %s\n", info
->ident
);
138 } else if (vpi_get(vpiSize
, info
->item
) == 1) {
139 fprintf(dump_file
, "x%s\n", info
->ident
);
141 fprintf(dump_file
, "bx %s\n", info
->ident
);
147 * managed qsorted list of scope names/variables for duplicates bsearching
150 struct vcd_names_list_s vcd_tab
= { 0 };
151 struct vcd_names_list_s vcd_var
= { 0 };
154 static int dumpvars_status
= 0; /* 0:fresh 1:cb installed, 2:callback done */
155 static PLI_UINT64 dumpvars_time
;
156 inline static int dump_header_pending(void)
158 return dumpvars_status
!= 2;
162 * This function writes out all the traced variables, whether they
165 static void vcd_checkpoint()
169 for (cur
= vcd_list
; cur
; cur
= cur
->next
)
173 static void vcd_checkpoint_x()
177 for (cur
= vcd_list
; cur
; cur
= cur
->next
)
178 show_this_item_x(cur
);
181 static PLI_INT32
variable_cb_2(p_cb_data cause
)
183 struct vcd_info
* info
= vcd_dmp_list
;
184 PLI_UINT64 now
= timerec_to_time64(cause
->time
);
186 if (now
!= vcd_cur_time
) {
187 fprintf(dump_file
, "#%" PLI_UINT64_FMT
"\n", now
);
192 show_this_item(info
);
194 } while ((info
= info
->dmp_next
) != 0);
201 static PLI_INT32
variable_cb_1(p_cb_data cause
)
204 struct vcd_info
*info
= (struct vcd_info
*)cause
->user_data
;
206 if (dump_is_full
) return 0;
207 if (dump_is_off
) return 0;
208 if (dump_header_pending()) return 0;
209 if (info
->scheduled
) return 0;
211 if ((dump_limit
> 0) && (ftell(dump_file
) > dump_limit
)) {
213 vpi_printf("WARNING: Dump file limit (%ld bytes) "
214 "exceeded.\n", dump_limit
);
215 fprintf(dump_file
, "$comment Dump file limit (%ld bytes) "
216 "exceeded. $end\n", dump_limit
);
222 cb
.reason
= cbReadOnlySynch
;
223 cb
.cb_rtn
= variable_cb_2
;
224 vpi_register_cb(&cb
);
228 info
->dmp_next
= vcd_dmp_list
;
234 static PLI_INT32
dumpvars_cb(p_cb_data cause
)
236 if (dumpvars_status
!= 1)
241 dumpvars_time
= timerec_to_time64(cause
->time
);
242 vcd_cur_time
= dumpvars_time
;
244 fprintf(dump_file
, "$enddefinitions $end\n");
247 fprintf(dump_file
, "#%" PLI_UINT64_FMT
"\n", dumpvars_time
);
248 fprintf(dump_file
, "$dumpvars\n");
250 fprintf(dump_file
, "$end\n");
256 static PLI_INT32
finish_cb(p_cb_data cause
)
258 if (finish_status
!= 0)
263 dumpvars_time
= timerec_to_time64(cause
->time
);
265 if (!dump_is_off
&& !dump_is_full
&& dumpvars_time
!= vcd_cur_time
) {
266 fprintf(dump_file
, "#%" PLI_UINT64_FMT
"\n", dumpvars_time
);
272 inline static int install_dumpvars_callback(void)
275 static struct t_vpi_time time
;
277 if (dumpvars_status
== 1)
280 if (dumpvars_status
== 2) {
281 vpi_mcd_printf(1, "VCD warning:"
282 " $dumpvars ignored,"
283 " previously called at simtime %" PLI_UINT64_FMT
"\n",
288 time
.type
= vpiSimTime
;
290 cb
.reason
= cbReadOnlySynch
;
291 cb
.cb_rtn
= dumpvars_cb
;
295 vpi_register_cb(&cb
);
297 cb
.reason
= cbEndOfSimulation
;
298 cb
.cb_rtn
= finish_cb
;
300 vpi_register_cb(&cb
);
306 static PLI_INT32
sys_dumpoff_calltf(PLI_BYTE8
*name
)
319 if (dump_header_pending())
322 now
.type
= vpiSimTime
;
323 vpi_get_time(0, &now
);
324 now64
= timerec_to_time64(&now
);
326 if (now64
> vcd_cur_time
)
327 fprintf(dump_file
, "#%" PLI_UINT64_FMT
"\n", now64
);
328 vcd_cur_time
= now64
;
330 fprintf(dump_file
, "$dumpoff\n");
332 fprintf(dump_file
, "$end\n");
337 static PLI_INT32
sys_dumpon_calltf(PLI_BYTE8
*name
)
350 if (dump_header_pending())
353 now
.type
= vpiSimTime
;
354 vpi_get_time(0, &now
);
355 now64
= timerec_to_time64(&now
);
357 if (now64
> vcd_cur_time
)
358 fprintf(dump_file
, "#%" PLI_UINT64_FMT
"\n", now64
);
359 vcd_cur_time
= now64
;
361 fprintf(dump_file
, "$dumpon\n");
363 fprintf(dump_file
, "$end\n");
368 static PLI_INT32
sys_dumpall_calltf(PLI_BYTE8
*name
)
376 if (dump_header_pending())
379 now
.type
= vpiSimTime
;
380 vpi_get_time(0, &now
);
381 now64
= timerec_to_time64(&now
);
383 if (now64
> vcd_cur_time
)
384 fprintf(dump_file
, "#%" PLI_UINT64_FMT
"\n", now64
);
385 vcd_cur_time
= now
.low
;
387 fprintf(dump_file
, "$dumpall\n");
389 fprintf(dump_file
, "$end\n");
394 static void open_dumpfile(void)
396 if (dump_path
== 0) {
397 dump_path
= strdup("dump.vcd");
400 dump_file
= fopen(dump_path
, "w");
402 if (dump_file
== 0) {
404 "VCD Error: Unable to open %s for output.\n",
408 int prec
= vpi_get(vpiTimePrecision
, 0);
414 "VCD info: dumpfile %s opened for output.\n",
429 fprintf(dump_file
, "$date\n");
430 fprintf(dump_file
, "\t%s",asctime(localtime(&walltime
)));
431 fprintf(dump_file
, "$end\n");
432 fprintf(dump_file
, "$version\n");
433 fprintf(dump_file
, "\tIcarus Verilog\n");
434 fprintf(dump_file
, "$end\n");
435 fprintf(dump_file
, "$timescale\n");
436 fprintf(dump_file
, "\t%u%s\n", scale
, units_names
[udx
]);
437 fprintf(dump_file
, "$end\n");
441 static PLI_INT32
sys_dumpfile_compiletf(PLI_BYTE8
*name
)
443 vpiHandle sys
= vpi_handle(vpiSysTfCall
, 0);
444 vpiHandle argv
= vpi_iterate(vpiArgument
, sys
);
449 if (argv
&& (item
= vpi_scan(argv
))) {
452 if (vpi_get(vpiType
, item
) != vpiConstant
453 || vpi_get(vpiConstType
, item
) != vpiStringConst
) {
456 " %s parameter must be a string constant\n",
461 value
.format
= vpiStringVal
;
462 vpi_get_value(item
, &value
);
463 path
= strdup(value
.value
.str
);
465 vpi_free_object(argv
);
468 path
= strdup("dump.vcd");
472 vpi_mcd_printf(1, "VCD Warning:"
473 " Overriding dumpfile path %s with %s\n",
483 static PLI_INT32
sys_dumpfile_calltf(PLI_BYTE8
*name
)
488 static PLI_INT32
sys_dumpflush_calltf(PLI_BYTE8
*name
)
496 static PLI_INT32
sys_dumplimit_compiletf(PLI_BYTE8
*name
)
498 vpiHandle callh
= vpi_handle(vpiSysTfCall
, 0);
499 vpiHandle argv
= vpi_iterate(vpiArgument
, callh
);
502 /* Check that there is a argument and get it. */
504 vpi_printf("ERROR: %s requires an argument.\n", name
);
505 vpi_control(vpiFinish
, 1);
508 limit
= vpi_scan(argv
);
510 /* Check that we are not given a string. */
511 switch (vpi_get(vpiType
, limit
)) {
514 if (vpi_get(vpiConstType
, limit
) == vpiStringConst
) {
515 vpi_printf("ERROR: %s's argument must be a number.\n", name
);
519 /* Check that there is only a single argument. */
520 limit
= vpi_scan(argv
);
522 vpi_printf("ERROR: %s takes a single argument.\n", name
);
523 vpi_control(vpiFinish
, 1);
530 static PLI_INT32
sys_dumplimit_calltf(PLI_BYTE8
*name
)
532 vpiHandle callh
= vpi_handle(vpiSysTfCall
, 0);
533 vpiHandle argv
= vpi_iterate(vpiArgument
, callh
);
534 vpiHandle limit
= vpi_scan(argv
);
537 /* Get the value and set the dump limit. */
538 val
.format
= vpiIntVal
;
539 vpi_get_value(limit
, &val
);
540 dump_limit
= val
.value
.integer
;
542 vpi_free_object(argv
);
546 static void scan_item(unsigned depth
, vpiHandle item
, int skip
)
549 struct vcd_info
* info
;
557 /* list of types to iterate upon */
559 static int types
[] = {
573 switch (vpi_get(vpiType
, item
)) {
576 /* don't know how to watch memories. */
580 /* There is nothing in named events to dump. */
583 case vpiNet
: type
= "wire"; if(0){
586 case vpiReg
: type
= "reg"; }
588 /* Skip this signal if it has already been included. */
589 if (vcd_names_search(&vcd_var
, vpi_get_str(vpiFullName
, item
))) {
590 vpi_printf("VCD warning: skipping signal %s, it was "
591 "previously included.\n",
592 vpi_get_str(vpiFullName
, item
));
599 name
= vpi_get_str(vpiName
, item
);
600 prefix
= is_escaped_id(name
) ? "\\" : "";
602 nexus_id
= vpi_get(_vpiNexusId
, item
);
605 ident
= find_nexus_ident(nexus_id
);
611 ident
= strdup(vcdid
);
615 set_nexus_ident(nexus_id
, ident
);
617 info
= malloc(sizeof(*info
));
619 info
->time
.type
= vpiSimTime
;
624 cb
.time
= &info
->time
;
625 cb
.user_data
= (char*)info
;
628 cb
.reason
= cbValueChange
;
629 cb
.cb_rtn
= variable_cb_1
;
632 info
->next
= vcd_list
;
636 info
->cb
= vpi_register_cb(&cb
);
639 fprintf(dump_file
, "$var %s %u %s %s%s",
640 type
, vpi_get(vpiSize
, item
), ident
,
643 if (vpi_get(vpiVector, item)
645 if (vpi_get(vpiSize
, item
) > 1
646 || vpi_get(vpiLeftRange
, item
) != 0) {
647 fprintf(dump_file
, " [%i:%i]", vpi_get(vpiLeftRange
, item
),
648 vpi_get(vpiRightRange
, item
));
650 fprintf(dump_file
, " $end\n");
655 /* Skip this signal if it has already been included. */
656 if (vcd_names_search(&vcd_var
, vpi_get_str(vpiFullName
, item
))) {
657 vpi_printf("VCD warning: skipping signal %s, it was "
658 "previously included.\n",
659 vpi_get_str(vpiFullName
, item
));
666 /* Declare the variable in the VCD file. */
667 name
= vpi_get_str(vpiName
, item
);
668 prefix
= is_escaped_id(name
) ? "\\" : "";
669 ident
= strdup(vcdid
);
671 fprintf(dump_file
, "$var real 1 %s %s%s $end\n",
672 ident
, prefix
, name
);
674 /* Add a callback for the variable. */
675 info
= malloc(sizeof(*info
));
677 info
->time
.type
= vpiSimTime
;
682 cb
.time
= &info
->time
;
683 cb
.user_data
= (char*)info
;
686 cb
.reason
= cbValueChange
;
687 cb
.cb_rtn
= variable_cb_1
;
689 info
->next
= vcd_list
;
693 info
->cb
= vpi_register_cb(&cb
);
697 case vpiModule
: type
= "module"; if(0){
698 case vpiNamedBegin
: type
= "begin"; }if(0){
699 case vpiTask
: type
= "task"; }if(0){
700 case vpiFunction
: type
= "function"; }if(0){
701 case vpiNamedFork
: type
= "fork"; }
707 const char* fullname
=
708 vpi_get_str(vpiFullName
, item
);
713 " scanning scope %s, %u levels\n",
716 nskip
= 0 != vcd_names_search(&vcd_tab
, fullname
);
719 vcd_names_add(&vcd_tab
, fullname
);
724 " in previously scanned scope %s\n",
727 name
= vpi_get_str(vpiName
, item
);
729 fprintf(dump_file
, "$scope %s %s $end\n", type
, name
);
731 for (i
=0; types
[i
]>0; i
++) {
733 argv
= vpi_iterate(types
[i
], item
);
734 while (argv
&& (hand
= vpi_scan(argv
))) {
735 scan_item(depth
-1, hand
, nskip
);
739 fprintf(dump_file
, "$upscope $end\n");
745 "VCD Error: $dumpvars: Unsupported parameter "
746 "type (%d)\n", vpi_get(vpiType
, item
));
750 static int draw_scope(vpiHandle item
)
756 vpiHandle scope
= vpi_handle(vpiScope
, item
);
760 depth
= 1 + draw_scope(scope
);
761 name
= vpi_get_str(vpiName
, scope
);
763 switch (vpi_get(vpiType
, item
)) {
764 case vpiNamedBegin
: type
= "begin"; break;
765 case vpiTask
: type
= "task"; break;
766 case vpiFunction
: type
= "function"; break;
767 case vpiNamedFork
: type
= "fork"; break;
768 default: type
= "module"; break;
771 fprintf(dump_file
, "$scope %s %s $end\n", type
, name
);
777 * This function is also used in sys_lxt to check the arguments of the
778 * lxt variant of $dumpvars.
780 PLI_INT32
sys_vcd_dumpvars_compiletf(PLI_BYTE8
*name
)
782 vpiHandle sys
= vpi_handle(vpiSysTfCall
, 0);
783 vpiHandle argv
= vpi_iterate(vpiArgument
, sys
);
789 tmp
= vpi_scan(argv
);
792 switch (vpi_get(vpiType
, tmp
)) {
794 if (vpi_get(vpiConstType
, tmp
) == vpiStringConst
) {
795 vpi_printf("ERROR: %s argument must be "
796 "a number constant.\n", name
);
797 vpi_control(vpiFinish
, 1);
808 vpi_printf("ERROR: %s argument must be "
809 "a number constant.\n", name
);
810 vpi_control(vpiFinish
, 1);
814 vpi_free_object(argv
);
818 static PLI_INT32
sys_dumpvars_calltf(PLI_BYTE8
*name
)
823 vpiHandle sys
= vpi_handle(vpiSysTfCall
, 0);
826 if (dump_file
== 0) {
832 if (install_dumpvars_callback()) {
836 argv
= vpi_iterate(vpiArgument
, sys
);
839 if (argv
&& (item
= vpi_scan(argv
)))
840 switch (vpi_get(vpiType
, item
)) {
846 value
.format
= vpiIntVal
;
847 vpi_get_value(item
, &value
);
848 depth
= value
.value
.integer
;
857 // search for the toplevel module
858 vpiHandle parent
= vpi_handle(vpiScope
, sys
);
861 parent
= vpi_handle(vpiScope
, item
);
864 } else if (!item
|| !(item
= vpi_scan(argv
))) {
867 // dump the current scope
868 item
= vpi_handle(vpiScope
, sys
);
872 for ( ; item
; item
= argv
? vpi_scan(argv
) : 0x0) {
876 vcd_names_sort(&vcd_tab
);
878 /* If this is a signal make sure it has not already
880 switch (vpi_get(vpiType
, item
)) {
886 scname
= vpi_get_str(vpiFullName
, vpi_handle(vpiScope
, item
));
887 if (vcd_names_search(&vcd_tab
, scname
)) {
888 vpi_mcd_printf(1, "VCD warning: skipping signal %s, "
889 "it was previously included.\n",
890 vpi_get_str(vpiFullName
, item
));
897 int dep
= draw_scope(item
);
899 scan_item(depth
, item
, 0);
902 fprintf(dump_file
, "$upscope $end\n");
905 /* Add this signal to the variable list so we can verify it
906 * is not included twice. This must be done after it has
909 vcd_names_add(&vcd_var
, vpi_get_str(vpiFullName
, item
));
910 vcd_names_sort(&vcd_var
);
917 void sys_vcd_register()
919 s_vpi_systf_data tf_data
;
921 tf_data
.type
= vpiSysTask
;
922 tf_data
.tfname
= "$dumpall";
923 tf_data
.calltf
= sys_dumpall_calltf
;
924 tf_data
.compiletf
= 0;
926 tf_data
.user_data
= "$dumpall";
927 vpi_register_systf(&tf_data
);
929 tf_data
.type
= vpiSysTask
;
930 tf_data
.tfname
= "$dumpoff";
931 tf_data
.calltf
= sys_dumpoff_calltf
;
932 tf_data
.compiletf
= 0;
934 tf_data
.user_data
= "$dumpoff";
935 vpi_register_systf(&tf_data
);
937 tf_data
.type
= vpiSysTask
;
938 tf_data
.tfname
= "$dumpon";
939 tf_data
.calltf
= sys_dumpon_calltf
;
940 tf_data
.compiletf
= 0;
942 tf_data
.user_data
= "$dumpon";
943 vpi_register_systf(&tf_data
);
945 tf_data
.type
= vpiSysTask
;
946 tf_data
.tfname
= "$dumpfile";
947 tf_data
.calltf
= sys_dumpfile_calltf
;
948 tf_data
.compiletf
= sys_dumpfile_compiletf
;
950 tf_data
.user_data
= "$dumpfile";
951 vpi_register_systf(&tf_data
);
953 tf_data
.type
= vpiSysTask
;
954 tf_data
.tfname
= "$dumpflush";
955 tf_data
.calltf
= sys_dumpflush_calltf
;
956 tf_data
.compiletf
= 0;
958 tf_data
.user_data
= "$dumpflush";
959 vpi_register_systf(&tf_data
);
961 tf_data
.type
= vpiSysTask
;
962 tf_data
.tfname
= "$dumplimit";
963 tf_data
.calltf
= sys_dumplimit_calltf
;
964 tf_data
.compiletf
= sys_dumplimit_compiletf
;
966 tf_data
.user_data
= "$dumplimit";
967 vpi_register_systf(&tf_data
);
969 tf_data
.type
= vpiSysTask
;
970 tf_data
.tfname
= "$dumpvars";
971 tf_data
.calltf
= sys_dumpvars_calltf
;
972 tf_data
.compiletf
= sys_vcd_dumpvars_compiletf
;
974 tf_data
.user_data
= "$dumpvars";
975 vpi_register_systf(&tf_data
);