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)
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_lxt.c,v 1.28 2007/03/14 04:05:51 steve Exp $"
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
33 # include "vpi_user.h"
42 # include "stringheap.h"
45 static enum lxm_optimum_mode_e
{
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.
61 struct lxt_scope
*next
, *prev
;
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
);
77 lxt_scope_head
= lxt_scope_current
= t
;
79 lxt_scope_current
->next
= t
;
80 t
->prev
= lxt_scope_current
;
81 lxt_scope_current
= t
;
85 static void pop_scope(void)
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;
103 * This function uses the scope stack to generate a hierarchical
104 * name. Scan the scope stack from the bottom up to construct the
107 static char *create_full_name(const char *name
)
111 struct lxt_scope
*t
= lxt_scope_head
;
113 /* Figure out how long the combined string will be. */
119 len
+= strlen(name
) + 1;
121 /* Allocate a string buffer. */
122 n
= n2
= malloc(len
);
135 assert( (n2
- n
+ 1) == len
);
141 static struct lt_trace
*dump_file
= 0;
146 struct t_vpi_time time
;
147 struct lt_symbol
*sym
;
148 struct vcd_info
*next
;
149 struct vcd_info
*dmp_next
;
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
)
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
);
173 value
.format
= vpiBinStrVal
;
174 vpi_get_value(info
->item
, &value
);
175 lt_emit_value_bit_string(dump_file
, info
->sym
,
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? */
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
210 static void vcd_checkpoint()
214 for (cur
= vcd_list
; cur
; cur
= cur
->next
)
218 static void vcd_checkpoint_x()
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
);
237 show_this_item(info
);
239 } while ((info
= info
->dmp_next
) != 0);
246 static PLI_INT32
variable_cb_1(p_cb_data cause
)
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
)) {
258 vpi_printf("WARNING: Dump file limit (%ld bytes) "
259 "exceeded.\n", dump_limit
);
265 cb
.reason
= cbReadOnlySynch
;
266 cb
.cb_rtn
= variable_cb_2
;
267 vpi_register_cb(&cb
);
271 info
->dmp_next
= vcd_dmp_list
;
277 static PLI_INT32
dumpvars_cb(p_cb_data cause
)
279 if (dumpvars_status
!= 1)
284 dumpvars_time
= timerec_to_time64(cause
->time
);
285 vcd_cur_time
= dumpvars_time
;
288 lt_set_time64(dump_file
, dumpvars_time
);
295 static PLI_INT32
finish_cb(p_cb_data cause
)
297 if (finish_status
!= 0)
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
);
311 inline static int install_dumpvars_callback(void)
314 static struct t_vpi_time time
;
316 if (dumpvars_status
== 1)
319 if (dumpvars_status
== 2) {
320 vpi_mcd_printf(1, "VCD Error:"
321 " $dumpvars ignored,"
322 " previously called at simtime %lu\n",
327 time
.type
= vpiSimTime
;
329 cb
.reason
= cbReadOnlySynch
;
330 cb
.cb_rtn
= dumpvars_cb
;
334 vpi_register_cb(&cb
);
336 cb
.reason
= cbEndOfSimulation
;
337 cb
.cb_rtn
= finish_cb
;
339 vpi_register_cb(&cb
);
345 static PLI_INT32
sys_dumpoff_calltf(PLI_BYTE8
*name
)
358 if (dump_header_pending())
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
);
375 static PLI_INT32
sys_dumpon_calltf(PLI_BYTE8
*name
)
388 if (dump_header_pending())
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
);
405 static PLI_INT32
sys_dumpall_calltf(PLI_BYTE8
*name
)
413 if (dump_header_pending())
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
;
429 static void *close_dumpfile(void)
432 return(dump_file
= NULL
);
435 static void open_dumpfile(const char*path
)
437 dump_file
= lt_init(path
);
439 if (dump_file
== 0) {
441 "LXT Error: Unable to open %s for output.\n",
445 int prec
= vpi_get(vpiTimePrecision
, 0);
448 "LXT info: dumpfile %s opened for output.\n",
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
)
465 vpiHandle sys
= vpi_handle(vpiSysTfCall
, 0);
466 vpiHandle argv
= vpi_iterate(vpiArgument
, sys
);
469 if (argv
&& (item
= vpi_scan(argv
))) {
472 if (vpi_get(vpiType
, item
) != vpiConstant
473 || vpi_get(vpiConstType
, item
) != vpiStringConst
) {
476 " %s parameter must be a string constant\n",
481 value
.format
= vpiStringVal
;
482 vpi_get_value(item
, &value
);
483 path
= strdup(value
.value
.str
);
485 vpi_free_object(argv
);
488 path
= strdup("dump.lxt");
494 assert(dump_file
== 0);
503 * The LXT1 format has no concept of file flushing.
505 static PLI_INT32
sys_dumpflush_calltf(PLI_BYTE8
*name
)
510 static PLI_INT32
sys_dumplimit_compiletf(PLI_BYTE8
*name
)
512 vpiHandle callh
= vpi_handle(vpiSysTfCall
, 0);
513 vpiHandle argv
= vpi_iterate(vpiArgument
, callh
);
516 /* Check that there is a argument and get it. */
518 vpi_printf("ERROR: %s requires an argument.\n", name
);
519 vpi_control(vpiFinish
, 1);
522 limit
= vpi_scan(argv
);
524 /* Check that we are not given a string. */
525 switch (vpi_get(vpiType
, limit
)) {
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
);
536 vpi_printf("ERROR: %s takes a single argument.\n", name
);
537 vpi_control(vpiFinish
, 1);
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
);
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
);
560 static void scan_item(unsigned depth
, vpiHandle item
, int skip
)
563 struct vcd_info
* info
;
570 /* list of types to iterate upon */
572 static int types
[] = {
586 switch (vpi_get(vpiType
, item
)) {
589 /* don't know how to watch memories. */
593 /* There is nothing in named events to dump. */
596 case vpiNet
: type
= "wire"; if(0){
599 case vpiReg
: type
= "reg"; }
604 name
= vpi_get_str(vpiName
, item
);
605 nexus_id
= vpi_get(_vpiNexusId
, item
);
607 ident
= find_nexus_ident(nexus_id
);
613 char*tmp
= create_full_name(name
);
614 ident
= strdup_sh(&name_heap
, tmp
);
618 set_nexus_ident(nexus_id
, ident
);
620 info
= malloc(sizeof(*info
));
622 info
->time
.type
= vpiSimTime
;
624 info
->sym
= lt_symbol_add(dump_file
, ident
, 0 /* array rows */, vpi_get(vpiLeftRange
, item
), vpi_get(vpiRightRange
, item
), LT_SYM_F_BITS
);
627 cb
.time
= &info
->time
;
628 cb
.user_data
= (char*)info
;
631 cb
.reason
= cbValueChange
;
632 cb
.cb_rtn
= variable_cb_1
;
634 info
->next
= vcd_list
;
637 info
->cb
= vpi_register_cb(&cb
);
640 char *n
= create_full_name(name
);
641 lt_symbol_alias(dump_file
, ident
, n
,
642 vpi_get(vpiSize
, item
)-1, 0);
653 name
= vpi_get_str(vpiName
, item
);
654 { char*tmp
= create_full_name(name
);
655 ident
= strdup_sh(&name_heap
, tmp
);
658 info
= malloc(sizeof(*info
));
660 info
->time
.type
= vpiSimTime
;
662 info
->sym
= lt_symbol_add(dump_file
, ident
, 0 /* array rows */, vpi_get(vpiSize
, item
)-1, 0, LT_SYM_F_DOUBLE
);
665 cb
.time
= &info
->time
;
666 cb
.user_data
= (char*)info
;
669 cb
.reason
= cbValueChange
;
670 cb
.cb_rtn
= variable_cb_1
;
672 info
->next
= vcd_list
;
675 info
->cb
= vpi_register_cb(&cb
);
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"; }
689 const char* fullname
=
690 vpi_get_str(vpiFullName
, item
);
695 " scanning scope %s, %u levels\n",
698 nskip
= 0 != vcd_names_search(&lxt_tab
, fullname
);
701 vcd_names_add(&lxt_tab
, fullname
);
706 " in previously scanned scope %s\n",
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
++) {
715 argv
= vpi_iterate(types
[i
], item
);
716 while (argv
&& (hand
= vpi_scan(argv
))) {
717 scan_item(depth
-1, hand
, nskip
);
727 "LXT Error: $lxtdumpvars: Unsupported parameter "
728 "type (%d)\n", vpi_get(vpiType
, item
));
733 static int draw_scope(vpiHandle item
)
739 vpiHandle scope
= vpi_handle(vpiScope
, item
);
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 */
759 static PLI_INT32
sys_dumpvars_calltf(PLI_BYTE8
*name
)
764 vpiHandle sys
= vpi_handle(vpiSysTfCall
, 0);
767 if (dump_file
== 0) {
768 open_dumpfile("dump.lxt");
773 if (install_dumpvars_callback()) {
777 argv
= vpi_iterate(vpiArgument
, sys
);
780 if (argv
&& (item
= vpi_scan(argv
)))
781 switch (vpi_get(vpiType
, item
)) {
787 value
.format
= vpiIntVal
;
788 vpi_get_value(item
, &value
);
789 depth
= value
.value
.integer
;
798 // search for the toplevel module
799 vpiHandle parent
= vpi_handle(vpiScope
, sys
);
802 parent
= vpi_handle(vpiScope
, item
);
805 } else if (!item
|| !(item
= vpi_scan(argv
))) {
808 // dump the current scope
809 item
= vpi_handle(vpiScope
, sys
);
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);
825 /* Most effective compression. */
826 if (lxm_optimum_mode
== LXM_SPACE
)
827 lt_set_no_interlace(dump_file
);
832 void sys_lxt_register()
835 struct t_vpi_vlog_info vlog_info
;
836 s_vpi_systf_data tf_data
;
839 /* Scan the extended arguments, looking for lxt optimization
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;
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;
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;
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;
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;
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
;
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
;
906 tf_data
.user_data
= "$dumpvars";
907 vpi_register_systf(&tf_data
);