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 LXT 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 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. */
120 len
+= strlen(name
) + 1;
121 if (is_esc_id
) len
+= 1;
123 /* Allocate a string buffer. */
124 n
= n2
= malloc(len
);
141 assert( (n2
- n
+ 1) == len
);
147 static struct lt_trace
*dump_file
= 0;
152 struct t_vpi_time time
;
153 struct lt_symbol
*sym
;
154 struct vcd_info
*next
;
155 struct vcd_info
*dmp_next
;
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
)
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
);
179 value
.format
= vpiBinStrVal
;
180 vpi_get_value(info
->item
, &value
);
181 lt_emit_value_bit_string(dump_file
, info
->sym
,
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? */
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
216 static void vcd_checkpoint()
220 for (cur
= vcd_list
; cur
; cur
= cur
->next
)
224 static void vcd_checkpoint_x()
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
);
243 show_this_item(info
);
245 } while ((info
= info
->dmp_next
) != 0);
252 static PLI_INT32
variable_cb_1(p_cb_data cause
)
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
)) {
264 vpi_printf("WARNING: Dump file limit (%ld bytes) "
265 "exceeded.\n", dump_limit
);
271 cb
.reason
= cbReadOnlySynch
;
272 cb
.cb_rtn
= variable_cb_2
;
273 vpi_register_cb(&cb
);
277 info
->dmp_next
= vcd_dmp_list
;
283 static PLI_INT32
dumpvars_cb(p_cb_data cause
)
285 if (dumpvars_status
!= 1)
290 dumpvars_time
= timerec_to_time64(cause
->time
);
291 vcd_cur_time
= dumpvars_time
;
294 lt_set_time64(dump_file
, dumpvars_time
);
301 static PLI_INT32
finish_cb(p_cb_data cause
)
303 if (finish_status
!= 0)
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
);
317 inline static int install_dumpvars_callback(void)
320 static struct t_vpi_time time
;
322 if (dumpvars_status
== 1)
325 if (dumpvars_status
== 2) {
326 vpi_mcd_printf(1, "LXT warning:"
327 " $dumpvars ignored,"
328 " previously called at simtime %lu\n",
333 time
.type
= vpiSimTime
;
335 cb
.reason
= cbReadOnlySynch
;
336 cb
.cb_rtn
= dumpvars_cb
;
340 vpi_register_cb(&cb
);
342 cb
.reason
= cbEndOfSimulation
;
343 cb
.cb_rtn
= finish_cb
;
345 vpi_register_cb(&cb
);
351 static PLI_INT32
sys_dumpoff_calltf(PLI_BYTE8
*name
)
364 if (dump_header_pending())
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
);
381 static PLI_INT32
sys_dumpon_calltf(PLI_BYTE8
*name
)
394 if (dump_header_pending())
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
);
411 static PLI_INT32
sys_dumpall_calltf(PLI_BYTE8
*name
)
419 if (dump_header_pending())
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
;
435 static void *close_dumpfile(void)
438 return(dump_file
= NULL
);
441 static void open_dumpfile(const char*path
)
443 dump_file
= lt_init(path
);
445 if (dump_file
== 0) {
447 "LXT Error: Unable to open %s for output.\n",
451 int prec
= vpi_get(vpiTimePrecision
, 0);
454 "LXT info: dumpfile %s opened for output.\n",
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
)
471 vpiHandle sys
= vpi_handle(vpiSysTfCall
, 0);
472 vpiHandle argv
= vpi_iterate(vpiArgument
, sys
);
475 if (argv
&& (item
= vpi_scan(argv
))) {
478 if (vpi_get(vpiType
, item
) != vpiConstant
479 || vpi_get(vpiConstType
, item
) != vpiStringConst
) {
482 " %s parameter must be a string constant\n",
487 value
.format
= vpiStringVal
;
488 vpi_get_value(item
, &value
);
489 path
= strdup(value
.value
.str
);
491 vpi_free_object(argv
);
494 path
= strdup("dump.lxt");
500 assert(dump_file
== 0);
509 * The LXT1 format has no concept of file flushing.
511 static PLI_INT32
sys_dumpflush_calltf(PLI_BYTE8
*name
)
516 static PLI_INT32
sys_dumplimit_compiletf(PLI_BYTE8
*name
)
518 vpiHandle callh
= vpi_handle(vpiSysTfCall
, 0);
519 vpiHandle argv
= vpi_iterate(vpiArgument
, callh
);
522 /* Check that there is a argument and get it. */
524 vpi_printf("ERROR: %s requires an argument.\n", name
);
525 vpi_control(vpiFinish
, 1);
528 limit
= vpi_scan(argv
);
530 /* Check that we are not given a string. */
531 switch (vpi_get(vpiType
, limit
)) {
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
);
542 vpi_printf("ERROR: %s takes a single argument.\n", name
);
543 vpi_control(vpiFinish
, 1);
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
);
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
);
566 static void scan_item(unsigned depth
, vpiHandle item
, int skip
)
569 struct vcd_info
* info
;
576 /* list of types to iterate upon */
578 static int types
[] = {
592 switch (vpi_get(vpiType
, item
)) {
595 /* don't know how to watch memories. */
599 /* There is nothing in named events to dump. */
602 case vpiNet
: type
= "wire"; if(0){
605 case vpiReg
: type
= "reg"; }
610 name
= vpi_get_str(vpiName
, item
);
611 nexus_id
= vpi_get(_vpiNexusId
, item
);
613 ident
= find_nexus_ident(nexus_id
);
619 char*tmp
= create_full_name(name
);
620 ident
= strdup_sh(&name_heap
, tmp
);
624 set_nexus_ident(nexus_id
, ident
);
626 info
= malloc(sizeof(*info
));
628 info
->time
.type
= vpiSimTime
;
630 info
->sym
= lt_symbol_add(dump_file
, ident
, 0 /* array rows */, vpi_get(vpiLeftRange
, item
), vpi_get(vpiRightRange
, item
), LT_SYM_F_BITS
);
633 cb
.time
= &info
->time
;
634 cb
.user_data
= (char*)info
;
637 cb
.reason
= cbValueChange
;
638 cb
.cb_rtn
= variable_cb_1
;
640 info
->next
= vcd_list
;
643 info
->cb
= vpi_register_cb(&cb
);
646 char *n
= create_full_name(name
);
647 lt_symbol_alias(dump_file
, ident
, n
,
648 vpi_get(vpiSize
, item
)-1, 0);
659 name
= vpi_get_str(vpiName
, item
);
660 { char*tmp
= create_full_name(name
);
661 ident
= strdup_sh(&name_heap
, tmp
);
664 info
= malloc(sizeof(*info
));
666 info
->time
.type
= vpiSimTime
;
668 info
->sym
= lt_symbol_add(dump_file
, ident
, 0 /* array rows */, vpi_get(vpiSize
, item
)-1, 0, LT_SYM_F_DOUBLE
);
671 cb
.time
= &info
->time
;
672 cb
.user_data
= (char*)info
;
675 cb
.reason
= cbValueChange
;
676 cb
.cb_rtn
= variable_cb_1
;
678 info
->next
= vcd_list
;
681 info
->cb
= vpi_register_cb(&cb
);
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"; }
695 const char* fullname
=
696 vpi_get_str(vpiFullName
, item
);
701 " scanning scope %s, %u levels\n",
704 nskip
= 0 != vcd_names_search(&lxt_tab
, fullname
);
707 vcd_names_add(&lxt_tab
, fullname
);
712 " in previously scanned scope %s\n",
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
++) {
721 argv
= vpi_iterate(types
[i
], item
);
722 while (argv
&& (hand
= vpi_scan(argv
))) {
723 scan_item(depth
-1, hand
, nskip
);
733 "LXT Error: $lxtdumpvars: Unsupported parameter "
734 "type (%d)\n", vpi_get(vpiType
, item
));
739 static int draw_scope(vpiHandle item
)
745 vpiHandle scope
= vpi_handle(vpiScope
, item
);
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 */
765 static PLI_INT32
sys_dumpvars_calltf(PLI_BYTE8
*name
)
770 vpiHandle sys
= vpi_handle(vpiSysTfCall
, 0);
773 if (dump_file
== 0) {
774 open_dumpfile("dump.lxt");
779 if (install_dumpvars_callback()) {
783 argv
= vpi_iterate(vpiArgument
, sys
);
786 if (argv
&& (item
= vpi_scan(argv
)))
787 switch (vpi_get(vpiType
, item
)) {
793 value
.format
= vpiIntVal
;
794 vpi_get_value(item
, &value
);
795 depth
= value
.value
.integer
;
804 // search for the toplevel module
805 vpiHandle parent
= vpi_handle(vpiScope
, sys
);
808 parent
= vpi_handle(vpiScope
, item
);
811 } else if (!item
|| !(item
= vpi_scan(argv
))) {
814 // dump the current scope
815 item
= vpi_handle(vpiScope
, sys
);
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);
831 /* Most effective compression. */
832 if (lxm_optimum_mode
== LXM_SPACE
)
833 lt_set_no_interlace(dump_file
);
838 void sys_lxt_register()
841 struct t_vpi_vlog_info vlog_info
;
842 s_vpi_systf_data tf_data
;
845 /* Scan the extended arguments, looking for lxt optimization
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;
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;
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;
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;
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;
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
;
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
;
912 tf_data
.user_data
= "$dumpvars";
913 vpi_register_systf(&tf_data
);