From acf4a3fa998875d832fd8b0660941b3d31f76bcc Mon Sep 17 00:00:00 2001 From: johnjohnlin Date: Tue, 28 Aug 2018 06:41:17 -0400 Subject: [PATCH] Add GTKWave LXT2 native tracing, bug1333. Signed-off-by: Wilson Snyder --- Changes | 2 + Makefile.in | 6 + bin/verilator | 45 +- include/lxt2/lxt2_config.h | 12 + include/lxt2/lxt2_write.cpp | 2206 +++++++++++++++++ include/lxt2/lxt2_write.h | 314 +++ include/lxt2/wavealloca.h | 29 + include/verilated.h | 2 + include/verilated_lxt2_c.cpp | 180 ++ include/verilated_lxt2_c.h | 204 ++ src/V3EmitC.cpp | 6 +- src/V3EmitMk.cpp | 8 +- src/V3Options.cpp | 2 + src/V3Options.h | 47 +- test_regress/driver.pl | 45 +- test_regress/t/t_dist_cinclude.pl | 3 +- test_regress/t/t_trace_array_lxt2.out | 57 + test_regress/t/t_trace_array_lxt2.pl | 26 + test_regress/t/t_trace_complex_lxt2.out | 193 ++ test_regress/t/t_trace_complex_lxt2.pl | 26 + .../t/t_trace_complex_params_lxt2.out | 193 ++ test_regress/t/t_trace_complex_params_lxt2.pl | 26 + .../t/t_trace_complex_structs_lxt2.out | 311 +++ .../t/t_trace_complex_structs_lxt2.pl | 26 + test_regress/t/t_trace_lxt2.out | 993 ++++++++ test_regress/t/t_trace_lxt2.pl | 22 + test_regress/t/t_trace_lxt2.v | 76 + test_regress/t/t_trace_packed_struct.v | 13 +- test_regress/t/t_trace_packed_struct_lxt2.out | 42 + test_regress/t/t_trace_packed_struct_lxt2.pl | 26 + test_regress/t/t_trace_param_lxt2.pl | 23 + test_regress/t/t_trace_primitive_lxt2.pl | 23 + test_regress/t/t_verilated_all.pl | 1 + 33 files changed, 5154 insertions(+), 34 deletions(-) create mode 100644 include/lxt2/lxt2_config.h create mode 100644 include/lxt2/lxt2_write.cpp create mode 100644 include/lxt2/lxt2_write.h create mode 100644 include/lxt2/wavealloca.h create mode 100644 include/verilated_lxt2_c.cpp create mode 100644 include/verilated_lxt2_c.h create mode 100644 test_regress/t/t_trace_array_lxt2.out create mode 100755 test_regress/t/t_trace_array_lxt2.pl create mode 100644 test_regress/t/t_trace_complex_lxt2.out create mode 100755 test_regress/t/t_trace_complex_lxt2.pl create mode 100644 test_regress/t/t_trace_complex_params_lxt2.out create mode 100755 test_regress/t/t_trace_complex_params_lxt2.pl create mode 100644 test_regress/t/t_trace_complex_structs_lxt2.out create mode 100755 test_regress/t/t_trace_complex_structs_lxt2.pl create mode 100644 test_regress/t/t_trace_lxt2.out create mode 100755 test_regress/t/t_trace_lxt2.pl create mode 100644 test_regress/t/t_trace_lxt2.v create mode 100644 test_regress/t/t_trace_packed_struct_lxt2.out create mode 100755 test_regress/t/t_trace_packed_struct_lxt2.pl create mode 100755 test_regress/t/t_trace_param_lxt2.pl create mode 100755 test_regress/t/t_trace_primitive_lxt2.pl diff --git a/Changes b/Changes index 7eb273ac0..e9ea58416 100644 --- a/Changes +++ b/Changes @@ -11,6 +11,8 @@ The contributors that suggested a given feature are shown in []. Thanks! ** Add runtime arguments. +** Add GTKWave LXT2 native tracing, bug1333. [Yu Sheng Lin] + ** Note $random has new algorithm; results may vary vs. previous versions. *** Better optimize large always block splitting, bug1244. [John Coiner] diff --git a/Makefile.in b/Makefile.in index a88e163fb..27967699d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -128,6 +128,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \ include/*.[chv]* \ include/*.in \ include/.*ignore \ + include/lxt2/*.[chv]* \ include/vltstd/*.[chv]* \ .*attributes */.*attributes */*/.*attributes \ src/.*ignore src/*.in src/*.cpp src/*.[chly] \ @@ -160,6 +161,7 @@ INST_PROJ_FILES = \ bin/verilator_profcfunc \ include/verilated.mk \ include/*.[chv]* \ + include/lxt2/*.[chv]* \ include/vltstd/*.[chv]* \ INST_PROJ_BIN_FILES = \ @@ -288,6 +290,7 @@ VL_INST_INC_BLDDIR_FILES = \ # Files under srcdir, instead of build time VL_INST_INC_SRCDIR_FILES = \ include/*.[chv]* \ + include/lxt2/*.[chv]* \ include/vltstd/*.[chv]* \ VL_INST_DATA_SRCDIR_FILES = \ @@ -315,6 +318,7 @@ installman: $(VL_INST_MAN_FILES) done installdata: + $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/include/lxt2 $(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/include/vltstd for p in $(VL_INST_INC_BLDDIR_FILES) ; do \ $(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \ @@ -344,6 +348,7 @@ uninstall: -cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_DATA_SRCDIR_FILES) -rm $(DESTDIR)$(pkgconfigdir)/verilator.pc -rmdir $(DESTDIR)$(pkgdatadir)/bin + -rmdir $(DESTDIR)$(pkgdatadir)/include/lxt2 -rmdir $(DESTDIR)$(pkgdatadir)/include/vltstd -rmdir $(DESTDIR)$(pkgdatadir)/include -rmdir $(DESTDIR)$(pkgdatadir)/examples/hello_world_c @@ -408,6 +413,7 @@ ifeq ($(CFG_WITH_DEFENV),yes) false endif @echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)" + $(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/include/lxt2 $(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/include/vltstd $(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/bin for p in $(INST_PROJ_FILES) ; do \ diff --git a/bin/verilator b/bin/verilator index 4cf0fe050..8f42da228 100755 --- a/bin/verilator +++ b/bin/verilator @@ -356,6 +356,7 @@ detailed descriptions in L for more information. --threads-max-mtasks Tune maximum mtask partitioning --top-module Name of top level input module --trace Enable waveform creation + --trace-lxt2 Enable LXT2 waveform creation --trace-depth Depth of tracing --trace-max-array Maximum bit width for tracing --trace-max-width Maximum array depth for tracing @@ -1240,17 +1241,23 @@ designs with only one top. =item --trace -Adds waveform tracing code to the model. Verilator will generate -additional {prefix}__Trace*.cpp files that will need to be compiled. In -addition verilated_vcd_sc.cpp (for SystemC traces) or verilated_vcd_c.cpp -(for both) must be compiled and linked in. If using the Verilator -generated Makefiles, these files will be added as source targets for you. -If you're not using the Verilator makefiles, you will need to add these to -your Makefile manually. +Adds waveform tracing code to the model using VCD format. This overrides +C<--trace-lxt2>. + +Verilator will generate additional {prefix}__Trace*.cpp files that will +need to be compiled. In addition verilated_vcd_sc.cpp (for SystemC traces) +or verilated_vcd_c.cpp (for both) must be compiled and linked in. If using +the Verilator generated Makefiles, these files will be added as source +targets for you. If you're not using the Verilator makefiles, you will +need to add these to your Makefile manually. Having tracing compiled in may result in some small performance losses, even when waveforms are not turned on during model execution. +=item --trace-lxt2 + +Enable LXT2 waveform tracing in the model. This overrides C<--trace>. + =item --trace-depth I Specify the number of levels deep to enable tracing, for example @@ -4098,11 +4105,29 @@ trace file if you want all data to land in the same output file. tfp->close(); } +=item How do I generate LXT2 waveforms (traces) in C++? + +LXT2 a format by GTKWave, which is usually 10x smaller than VCD format. +This version provides a basic LXT2 support. +To dump LXT2 format, add the --trace switch to Verilator and change the include path in the testbench to: + + #include "verilated_lxt2_c.h" + VerilatedLxt2C* tfp = new VerilatedLxt2C; + +Note that currently supporting both LXT2 and VCD in a single simulation is impossible, +but such requirement could be rare. + +=item How do I generate LXT2 waveforms (traces) in SystemC? + +The LXT2 library from GTKWave does not currently support SystemC; use VCD +format instead. + =item How do I view waveforms (traces)? -Verilator makes standard VCD (Value Change Dump) files. They are viewable -with the public domain GtkWave (recommended) or Dinotrace (legacy) -programs, or any of the many commercial offerings. +Verilator makes standard VCD (Value Change Dump) and LXT2 files. VCD files are viewable +with the public domain GTKWave (recommended) or Dinotrace (legacy) +programs, or any of the many commercial offerings; +LXT2 is supported by GTKWave only. =item How do I reduce the size of large waveform (trace) files? diff --git a/include/lxt2/lxt2_config.h b/include/lxt2/lxt2_config.h new file mode 100644 index 000000000..653a19217 --- /dev/null +++ b/include/lxt2/lxt2_config.h @@ -0,0 +1,12 @@ +/* This file specifically for LXT2 usage */ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 diff --git a/include/lxt2/lxt2_write.cpp b/include/lxt2/lxt2_write.cpp new file mode 100644 index 000000000..da5addd06 --- /dev/null +++ b/include/lxt2/lxt2_write.cpp @@ -0,0 +1,2206 @@ +/* + * Copyright (c) 2003-2016 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef _AIX +#pragma alloca +#endif + +#include "lxt2_config.h" +#include "lxt2_write.h" + +static char *lxt2_wr_vcd_truncate_bitvec(char *s) +{ +char l, r; + +r=*s; +if(r=='1') + { + return s; + } + else + { + s++; + } + +for(;;s++) + { + l=r; r=*s; + if(!r) return (s-1); + + if(l!=r) + { + return(((l=='0')&&(r=='1'))?s:s-1); + } + } +} + + +/* + * in-place sort to keep chained facs from migrating... + */ +static void wave_mergesort(struct lxt2_wr_symbol **a, struct lxt2_wr_symbol **b, int lo, int hi) +{ +int i, j, k; + +if (loname, a[j]->name) <= 0) + { + a[k++]=b[i++]; + } + else + { + a[k++]=a[j++]; + } + } + + while (kitem) { + if (t->left == NULL) break; + if (i < t->left->item) { + y = t->left; /* rotate right */ + t->left = y->right; + y->right = t; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + } else if (i > t->item) { + if (t->right == NULL) break; + if (i > t->right->item) { + y = t->right; /* rotate left */ + t->right = y->left; + y->left = t; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + } else { + break; + } + } + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + return t; +} + + +static lxt2_wr_ds_Tree * lxt2_wr_ds_insert(granmsk_t i, lxt2_wr_ds_Tree * t, int val) { +/* Insert i into the tree t, unless it's already there. */ +/* Return a pointer to the resulting tree. */ + lxt2_wr_ds_Tree * n; + + n = (lxt2_wr_ds_Tree *) calloc (1, sizeof (lxt2_wr_ds_Tree)); + if (n == NULL) { + fprintf(stderr, "ds_insert: ran out of memory, exiting.\n"); + exit(255); + } + n->item = i; + n->val = val; + if (t == NULL) { + n->left = n->right = NULL; + return n; + } + t = lxt2_wr_ds_splay(i,t); + if (i < t->item) { + n->left = t->left; + n->right = t; + t->left = NULL; + return n; + } else if (i > t->item) { + n->right = t->right; + n->left = t; + t->right = NULL; + return n; + } else { /* We get here if it's already in the tree */ + /* Don't add it again */ + free(n); + return t; + } +} + +/************************ splay ************************/ + +static int lxt2_wr_dslxt_success; + +static lxt2_wr_dslxt_Tree * lxt2_wr_dslxt_splay (char *i, lxt2_wr_dslxt_Tree * t) { +/* Simple top down splay, not requiring i to be in the tree t. */ +/* What it does is described above. */ + lxt2_wr_dslxt_Tree N, *l, *r, *y; + int dir; + + lxt2_wr_dslxt_success = 0; + + if (t == NULL) return t; + N.left = N.right = NULL; + l = r = &N; + + for (;;) { + dir = strcmp(i, t->item); + if (dir < 0) { + if (t->left == NULL) break; + if (strcmp(i, t->left->item)<0) { + y = t->left; /* rotate right */ + t->left = y->right; + y->right = t; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + } else if (dir > 0) { + if (t->right == NULL) break; + if (strcmp(i, t->right->item)>0) { + y = t->right; /* rotate left */ + t->right = y->left; + y->left = t; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + } else { + lxt2_wr_dslxt_success=1; + break; + } + } + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + return t; +} + + +static lxt2_wr_dslxt_Tree * lxt2_wr_dslxt_insert(char *i, lxt2_wr_dslxt_Tree * t, unsigned int val) { +/* Insert i into the tree t, unless it's already there. */ +/* Return a pointer to the resulting tree. */ + lxt2_wr_dslxt_Tree * n; + int dir; + + n = (lxt2_wr_dslxt_Tree *) calloc (1, sizeof (lxt2_wr_dslxt_Tree)); + if (n == NULL) { + fprintf(stderr, "dslxt_insert: ran out of memory, exiting.\n"); + exit(255); + } + n->item = i; + n->val = val; + if (t == NULL) { + n->left = n->right = NULL; + return n; + } + t = lxt2_wr_dslxt_splay(i,t); + dir = strcmp(i,t->item); + if (dir<0) { + n->left = t->left; + n->right = t; + t->left = NULL; + return n; + } else if (dir>0) { + n->right = t->right; + n->left = t; + t->right = NULL; + return n; + } else { /* We get here if it's already in the tree */ + /* Don't add it again */ + free(n); + return t; + } +} + +/************************ splay ************************/ + +/* + * functions which emit various big endian + * data to a file + */ +static int lxt2_wr_emit_u8(struct lxt2_wr_trace *lt, int value) +{ +unsigned char buf[1]; +int nmemb; + +buf[0] = value & 0xff; +nmemb=fwrite(buf, sizeof(char), 1, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lxt2_wr_emit_u16(struct lxt2_wr_trace *lt, int value) +{ +unsigned char buf[2]; +int nmemb; + +buf[0] = (value>>8) & 0xff; +buf[1] = value & 0xff; +nmemb = fwrite(buf, sizeof(char), 2, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lxt2_wr_emit_u32(struct lxt2_wr_trace *lt, int value) +{ +unsigned char buf[4]; +int nmemb; + +buf[0] = (value>>24) & 0xff; +buf[1] = (value>>16) & 0xff; +buf[2] = (value>>8) & 0xff; +buf[3] = value & 0xff; +nmemb=fwrite(buf, sizeof(char), 4, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lxt2_wr_emit_u64(struct lxt2_wr_trace *lt, int valueh, int valuel) +{ +int rc; + +if((rc=lxt2_wr_emit_u32(lt, valueh))) + { + rc=lxt2_wr_emit_u32(lt, valuel); + } + +return(rc); +} + + +/* + * gzfunctions which emit various big endian + * data to a file. (lt->position needs to be + * fixed up on gzclose so the tables don't + * get out of sync!) + */ +static int gzwrite_buffered(struct lxt2_wr_trace *lt) +{ +int rc = 1; + +if(lt->gzbufpnt > LXT2_WR_GZWRITE_BUFFER) + { + rc = gzwrite(lt->zhandle, lt->gzdest, lt->gzbufpnt); + rc = rc ? 1 : 0; + lt->gzbufpnt = 0; + } + +return(rc); +} + +static void gzflush_buffered(struct lxt2_wr_trace *lt, int doclose) +{ +if(lt->gzbufpnt) + { + gzwrite(lt->zhandle, lt->gzdest, lt->gzbufpnt); + lt->gzbufpnt = 0; + if(!doclose) + { + gzflush(lt->zhandle, Z_SYNC_FLUSH); + } + } + +if(doclose) + { + gzclose(lt->zhandle); + } +} + + +static int lxt2_wr_emit_u8z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = value & 0xff; + +nmemb=gzwrite_buffered(lt); +lt->zpackcount++; +lt->position++; +return(nmemb); +} + + +static int lxt2_wr_emit_u16z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; +lt->gzdest[lt->gzbufpnt++] = value & 0xff; +nmemb = gzwrite_buffered(lt); +lt->zpackcount+=2; +lt->position+=2; +return(nmemb); +} + + +static int lxt2_wr_emit_u24z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = (value>>16) & 0xff; +lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; +lt->gzdest[lt->gzbufpnt++] = value & 0xff; +nmemb=gzwrite_buffered(lt); +lt->zpackcount+=3; +lt->position+=3; +return(nmemb); +} + + +static int lxt2_wr_emit_u32z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = (value>>24) & 0xff; +lt->gzdest[lt->gzbufpnt++] = (value>>16) & 0xff; +lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; +lt->gzdest[lt->gzbufpnt++] = value & 0xff; +nmemb=gzwrite_buffered(lt); + +lt->zpackcount+=4; +lt->position+=4; +return(nmemb); +} + + +static int lxt2_wr_emit_u64z(struct lxt2_wr_trace *lt, int valueh, int valuel) +{ +int rc; + +if((rc=lxt2_wr_emit_u32z(lt, valueh))) + { + rc=lxt2_wr_emit_u32z(lt, valuel); + } + +return(rc); +} + + +static int lxt2_wr_emit_stringz(struct lxt2_wr_trace *lt, char *value) +{ +int rc=1; +do + { + rc&=lxt2_wr_emit_u8z(lt, *value); + } while(*(value++)); +return(rc); +} + + +/* + * hash/symtable manipulation + */ +static int lxt2_wr_hash(const char *s) +{ +const char *p; +char ch; +unsigned int h=0, h2=0, pos=0, g; +for(p=s;*p;p++) + { + ch=*p; + h2<<=3; + h2-=((unsigned int)ch+(pos++)); /* this handles stranded vectors quite well.. */ + + h=(h<<4)+ch; + if((g=h&0xf0000000)) + { + h=h^(g>>24); + h=h^g; + } + } + +h^=h2; /* combine the two hashes */ +return(h%LXT2_WR_SYMPRIME); +} + + +static struct lxt2_wr_symbol *lxt2_wr_symadd(struct lxt2_wr_trace *lt, const char *name, int hv) +{ +struct lxt2_wr_symbol *s; + +s=(struct lxt2_wr_symbol *)calloc(1,sizeof(struct lxt2_wr_symbol)); +strcpy(s->name=(char *)malloc((s->namlen=strlen(name))+1),name); +s->next=lt->sym[hv]; +lt->sym[hv]=s; +return(s); +} + + +static struct lxt2_wr_symbol *lxt2_wr_symfind(struct lxt2_wr_trace *lt, const char *s) +{ +int hv; +struct lxt2_wr_symbol *temp; + +hv=lxt2_wr_hash(s); +if(!(temp=lt->sym[hv])) return(NULL); /* no hash entry, add here wanted to add */ + +while(temp) + { + if(!strcmp(temp->name,s)) + { + return(temp); /* in table already */ + } + if(!temp->next) break; + temp=temp->next; + } + +return(NULL); /* not found, add here if you want to add*/ +} + + +/* + * compress facs to a prefix count + string + 0x00 + */ +static void lxt2_wr_compress_fac(struct lxt2_wr_trace *lt, char *str) +{ +int i; +int len = strlen(str); +int minlen = (lencompress_fac_len) ? len : lt->compress_fac_len; + +if(minlen>65535) minlen=65535; /* keep in printable range--most hierarchies won't be this big anyway */ + +if(lt->compress_fac_str) + { + for(i=0;icompress_fac_str[i]!=str[i]) break; + } + lxt2_wr_emit_u16z(lt, i); + lxt2_wr_emit_stringz(lt, str+i); + free(lt->compress_fac_str); + } + else + { + lxt2_wr_emit_u16z(lt, 0); + lxt2_wr_emit_stringz(lt, str); + } + +lt->compress_fac_str = (char *) malloc((lt->compress_fac_len=len)+1); +strcpy(lt->compress_fac_str, str); +} + + +/* + * emit facs in sorted order along with geometry + * and sync table info + */ + +static void strip_brack(struct lxt2_wr_symbol *s) +{ +char *lastch = s->name+s->namlen - 1; +if(*lastch!=']') return; +if(s->namlen<3) return; +lastch--; +while(lastch!=s->name) + { + if(*lastch=='.') + { + return; /* MTI SV [0.3] notation for implicit vars */ + } + + if(*lastch=='[') + { + *lastch=0x00; + return; + } + lastch--; + } +return; +} + + +static void lxt2_wr_emitfacs(struct lxt2_wr_trace *lt) +{ +unsigned int i; + +if((lt)&&(lt->numfacs)) + { + struct lxt2_wr_symbol *s = lt->symchain; + struct lxt2_wr_symbol **aliascache = (lxt2_wr_symbol**)calloc(lt->numalias ? lt->numalias : 1, sizeof(struct lxt2_wr_symbol *)); + unsigned int aliases_encountered, facs_encountered; + + lt->sorted_facs = (struct lxt2_wr_symbol **)calloc(lt->numfacs, sizeof(struct lxt2_wr_symbol *)); + + if(lt->sorted_facs && aliascache) + { + if(lt->do_strip_brackets) + for(i=0;inumfacs;i++) + { + lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ + strip_brack(s); + s=s->symchain; + } + else + for(i=0;inumfacs;i++) + { + lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ + s=s->symchain; + } + wave_msort(lt->sorted_facs, lt->numfacs); + + if(lt->partial_preference) + { + /* move preferenced facs up */ + struct lxt2_wr_symbol **prefcache = aliascache; + int prefs_encountered = 0; + + facs_encountered = 0; + for(i=0;inumfacs;i++) + { + if((lt->sorted_facs[i]->partial_preference)==0) + { + lt->sorted_facs[facs_encountered] = lt->sorted_facs[i]; + facs_encountered++; + } + else + { + prefcache[prefs_encountered] = lt->sorted_facs[i]; + prefs_encountered++; + } + } + /* then append the non-preferenced facs */ + for(i=0;isorted_facs[i]; + } + + /* now make prefcache the main cache */ + aliascache = lt->sorted_facs; + lt->sorted_facs = prefcache; + } + + /* move facs up */ + aliases_encountered = 0, facs_encountered = 0; + for(i=0;inumfacs;i++) + { + if((lt->sorted_facs[i]->flags&LXT2_WR_SYM_F_ALIAS)==0) + { + lt->sorted_facs[facs_encountered] = lt->sorted_facs[i]; + facs_encountered++; + } + else + { + aliascache[aliases_encountered] = lt->sorted_facs[i]; + aliases_encountered++; + } + } + /* then append the aliases */ + for(i=0;isorted_facs[facs_encountered+i] = aliascache[i]; + } + + + for(i=0;inumfacs;i++) + { + lt->sorted_facs[i]->facnum = i; + } + + if(!lt->timezero) + { + lxt2_wr_emit_u32(lt, lt->numfacs); /* uncompressed */ + } + else + { + lxt2_wr_emit_u32(lt, 0); /* uncompressed, flag to insert extra parameters */ + lxt2_wr_emit_u32(lt, 8); /* uncompressed 8 counts timezero and on */ + lxt2_wr_emit_u32(lt, lt->numfacs); /* uncompressed */ + lxt2_wr_emit_u64(lt, (lt->timezero >> 32) & 0xffffffffL, lt->timezero & 0xffffffffL); /* uncompressed */ + } + + + lxt2_wr_emit_u32(lt, lt->numfacbytes); /* uncompressed */ + lxt2_wr_emit_u32(lt, lt->longestname); /* uncompressed */ + + lt->facname_offset=lt->position; + + lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacnamesize */ + lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacname_predec_size */ + lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacgeometrysize */ + lxt2_wr_emit_u8(lt, lt->timescale); /* timescale (-9 default == nsec) */ + + fflush(lt->handle); + lt->zfacname_size = lt->position; + lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); + + lt->zpackcount = 0; + for(i=0;inumfacs;i++) + { + lxt2_wr_compress_fac(lt, lt->sorted_facs[i]->name); + free(lt->sorted_facs[i]->name); + lt->sorted_facs[i]->name = NULL; + } + free(lt->compress_fac_str); lt->compress_fac_str=NULL; + lt->compress_fac_len=0; + lt->zfacname_predec_size = lt->zpackcount; + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + lt->zfacname_size = lt->position - lt->zfacname_size; + + lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); + + lt->facgeometry_offset = lt->position; + for(i=0;inumfacs;i++) + { + if((lt->sorted_facs[i]->flags&LXT2_WR_SYM_F_ALIAS)==0) + { + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->rows); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->msb); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->lsb); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->flags); + } + else + { + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->aliased_to->facnum); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->msb); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->lsb); + lxt2_wr_emit_u32z(lt, LXT2_WR_SYM_F_ALIAS); + } + } + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + lt->break_header_size = lt->position; /* in case we need to emit multiple lxt2s with same header */ + lt->zfacgeometry_size = lt->position - lt->facgeometry_offset; + + fseeko(lt->handle, lt->facname_offset, SEEK_SET); + lxt2_wr_emit_u32(lt, lt->zfacname_size); /* backpatch sizes... */ + lxt2_wr_emit_u32(lt, lt->zfacname_predec_size); + lxt2_wr_emit_u32(lt, lt->zfacgeometry_size); + + lt->numfacs = facs_encountered; /* don't process alias value changes ever */ + } + + free(aliascache); + } +} + + +/* + * initialize the trace and get back an lt context + */ +struct lxt2_wr_trace *lxt2_wr_init(const char *name) +{ +struct lxt2_wr_trace *lt=(struct lxt2_wr_trace *)calloc(1, sizeof(struct lxt2_wr_trace)); + +if((!name)||(!(lt->handle=fopen(name, "wb")))) + { + free(lt); + lt=NULL; + } + else + { + lt->lxtname = strdup(name); + + lxt2_wr_emit_u16(lt, LXT2_WR_HDRID); + lxt2_wr_emit_u16(lt, LXT2_WR_VERSION); + lxt2_wr_emit_u8 (lt, LXT2_WR_GRANULE_SIZE); /* currently 32 or 64 */ + lt->timescale = -9; + lt->maxgranule = LXT2_WR_GRANULE_NUM; + lxt2_wr_set_compression_depth(lt, 4); /* set fast/loose compression depth, user can fix this any time after init */ + lt->initial_value = 'x'; + } + +return(lt); +} + + +/* + * setting break size + */ +void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz) +{ +if(lt) + { + lt->break_size = siz; + } +} + + +/* + * enable/disable partial dump mode (for faster reads) + */ +void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt) +{ +if(lt) + { + lt->partial = 0; + lt->partial_zip = 0; + } +} + +void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode) +{ +if(lt) + { + lt->partial = 1; + lt->partial_zip = (zipmode != 0); + lt->partial_iter = LXT2_WR_PARTIAL_SIZE; + } +} + +void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name) +{ +struct lxt2_wr_symbol *s; + +if((lt)&&(name)&&(!lt->sorted_facs)) + { + s=lxt2_wr_symfind(lt, name); + if(s) + { + while(s->aliased_to) /* find root alias */ + { + s=s->aliased_to; + } + + s->partial_preference = 1; + } + } +} + + +/* + * enable/disable checkpointing (for smaller files) + */ +void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt) +{ +if(lt) + { + lt->no_checkpoint = 1; + } +} + +void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt) +{ +if(lt) + { + lt->no_checkpoint = 0; + } +} + + +/* + * set initial value of trace (0, 1, x, z) only legal vals + */ +void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value) +{ +if(lt) + { + switch(value) + { + case '0': + case '1': + case 'x': + case 'z': break; + case 'Z': value = 'z'; break; + default: value = 'x'; break; + } + + lt->initial_value = value; + } +} + + +/* + * maint function for finding a symbol if it exists + */ +struct lxt2_wr_symbol *lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name) +{ +struct lxt2_wr_symbol *s=NULL; + +if((lt)&&(name)) s=lxt2_wr_symfind(lt, name); +return(s); +} + + +/* + * add a trace (if it doesn't exist already) + */ +struct lxt2_wr_symbol *lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags) +{ +struct lxt2_wr_symbol *s; +int len; +int flagcnt; + +if((!lt)||(lt->sorted_facs)) return(NULL); + +flagcnt = ((flags&LXT2_WR_SYM_F_INTEGER)!=0) + ((flags&LXT2_WR_SYM_F_DOUBLE)!=0) + ((flags&LXT2_WR_SYM_F_STRING)!=0); + +if((flagcnt>1)||(!lt)||(!name)||(lxt2_wr_symfind(lt, name))) return (NULL); + +s=lxt2_wr_symadd(lt, name, lxt2_wr_hash(name)); +s->rows = rows; +s->flags = flags&(~LXT2_WR_SYM_F_ALIAS); /* aliasing makes no sense here.. */ + +if(!flagcnt) + { + s->msb = msb; + s->lsb = lsb; + s->len = (msbvalue = strdup("NaN"); + } + else + { + if(flags & LXT2_WR_SYM_F_INTEGER) + { + s->len = 32; + } + + s->value = (char*)malloc(s->len + 1); + memset(s->value, lt->initial_value, s->len); + s->value[s->len]=0; + + s->msk = LXT2_WR_GRAN_1VAL; /* stuff in an initial value */ + switch(lt->initial_value) + { + case '0': s->chg[0] = LXT2_WR_ENC_0; break; + case '1': s->chg[0] = LXT2_WR_ENC_1; break; + case 'z': s->chg[0] = LXT2_WR_ENC_Z; break; + default: s->chg[0] = LXT2_WR_ENC_X; break; + } + s->chgpos++; /* don't worry that a time doesn't exist as it will soon enough.. */ + } + +s->symchain = lt->symchain; +lt->symchain = s; + +lt->numfacs++; + +if((len=strlen(name)) > lt->longestname) lt->longestname = len; +lt->numfacbytes += (len+1); + +return(s); +} + +/* + * add an alias trace (if it doesn't exist already and orig is found) + */ +struct lxt2_wr_symbol *lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb) +{ +struct lxt2_wr_symbol *s, *sa; +int len; +int bitlen; +int flagcnt; + +if((!lt)||(!existing_name)||(!alias)||(!(s=lxt2_wr_symfind(lt, existing_name)))||(lxt2_wr_symfind(lt, alias))) return (NULL); + +if(lt->sorted_facs) return(NULL); + +while(s->aliased_to) /* find root alias */ + { + s=s->aliased_to; + } + +flagcnt = ((s->flags&LXT2_WR_SYM_F_INTEGER)!=0) + ((s->flags&LXT2_WR_SYM_F_DOUBLE)!=0) + ((s->flags&LXT2_WR_SYM_F_STRING)!=0); +bitlen = (msblen)) return(NULL); + +sa=lxt2_wr_symadd(lt, alias, lxt2_wr_hash(alias)); +sa->flags = LXT2_WR_SYM_F_ALIAS; /* only point this can get set */ +sa->aliased_to = s; + +if(!flagcnt) + { + sa->msb = msb; + sa->lsb = lsb; + sa->len = bitlen; + } + +sa->symchain = lt->symchain; +lt->symchain = sa; +lt->numfacs++; +lt->numalias++; +if((len=strlen(alias)) > lt->longestname) lt->longestname = len; +lt->numfacbytes += (len+1); + +return(sa); +} + + +/* + * set current time/granule updating + */ +int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval) +{ +return(lxt2_wr_set_time64(lt, lt->maxtime + (lxttime_t)timeval)); +} + +int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval) +{ +return(lxt2_wr_set_time64(lt, (lxttime_t)timeval)); +} + +int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval) +{ +return(lxt2_wr_set_time64(lt, lt->maxtime + timeval)); +} + + +/* + * file size limiting/header cloning... + */ +static void lxt2_wr_emit_do_breakfile(struct lxt2_wr_trace *lt) +{ +unsigned int len = strlen(lt->lxtname); +int i; +char *tname = (char*)malloc(len + 30); +FILE *f2, *clone; +off_t cnt, seg; +char buf[32768]; + +for(i=len;i>0;i--) + { + if(lt->lxtname[i]=='.') break; + } + +if(!i) + { + sprintf(tname, "%s_%03u.lxt", lt->lxtname, ++lt->break_number); + } + else + { + memcpy(tname, lt->lxtname, i); + sprintf(tname+i, "_%03u.lxt", ++lt->break_number); + } + +f2 = fopen(tname, "wb"); +if(!f2) /* if error, keep writing to same output file...sorry */ + { + free(tname); + return; + } + +clone = fopen(lt->lxtname, "rb"); +if(!clone) + { /* this should never happen */ + fclose(f2); + unlink(tname); + free(tname); + return; + } + +/* clone original header */ +for(cnt = 0; cnt < lt->break_header_size; cnt += sizeof(buf)) + { + seg = lt->break_header_size - cnt; + if(seg > (off_t)sizeof(buf)) + { + seg = sizeof(buf); + } + + if(fread(buf, seg, 1, clone)) + { + if(!fwrite(buf, seg, 1, f2)) break; /* write error! */ + } + } + +fclose(clone); +fclose(lt->handle); +lt->handle = f2; +free(tname); +} + + +/* + * emit granule + */ +void lxt2_wr_flush_granule(struct lxt2_wr_trace *lt, int do_finalize) +{ +unsigned int idx_nbytes, map_nbytes, i, j; +struct lxt2_wr_symbol *s; +unsigned int partial_iter; +unsigned int iter, iter_hi; +unsigned char using_partial, using_partial_zip=0; +off_t current_iter_pos=0; +int early_flush; + +if(lt->flush_valid) + { + if(lt->flushtime == lt->lasttime) + { + return; + } + + lt->flush_valid = 0; + } + +lt->granule_dirty = 0; + +if((using_partial=(lt->partial)&&(lt->numfacs>lt->partial_iter))) + { + partial_iter = lt->partial_iter; + using_partial_zip = lt->partial_zip; + } + else + { + partial_iter = lt->numfacs; + } + +if(!lt->timegranule) + { + int attempt_break_state = 2; + + do { + fseeko(lt->handle, 0L, SEEK_END); + lt->current_chunk=lt->position = ftello(lt->handle); + + if((lt->break_size)&&(attempt_break_state==2)&&(lt->position >= lt->break_size)&&(lt->position != lt->break_header_size)) + { + lxt2_wr_emit_do_breakfile(lt); + attempt_break_state--; + } + else + { + attempt_break_state = 0; + } + } while(attempt_break_state); + + /* fprintf(stderr, "First chunk position is %d (0x%08x)\n", lt->current_chunk, lt->current_chunk); */ + lxt2_wr_emit_u32(lt, 0); /* size of this section (uncompressed) */ + lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ + lxt2_wr_emit_u64(lt, 0, 0); /* begin time of section */ + lxt2_wr_emit_u64(lt, 0, 0); /* end time of section */ + fflush(lt->handle); + lt->current_chunkz = lt->position; + /* fprintf(stderr, "First chunkz position is %d (0x%08x)\n", lt->current_chunkz, lt->current_chunkz); */ + + if(!using_partial_zip) + { + lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); + } + else + { + lt->zpackcount_cumulative = 0; + } + lt->zpackcount = 0; + } + + + +for(iter=0; iternumfacs; iter=iter_hi) +{ +unsigned int total_chgs; +unsigned int partial_length; + +total_chgs = 0; +/* partial_length = 0; */ /* scan-build : never read */ + +iter_hi = iter + partial_iter; +if(iter_hi > lt->numfacs) iter_hi = lt->numfacs; + +for(j=iter;jsorted_facs[j]->msk; + + lt->mapdict = lxt2_wr_ds_splay (msk, lt->mapdict); + if((!lt->mapdict)||(lt->mapdict->item != msk)) + { + lt->mapdict = lxt2_wr_ds_insert(msk, lt->mapdict, lt->num_map_entries); + lt->num_map_entries++; + + if(lt->mapdict_curr) + { + lt->mapdict_curr->next = lt->mapdict; + lt->mapdict_curr = lt->mapdict; + } + else + { + lt->mapdict_head = lt->mapdict_curr = lt->mapdict; + } + } + } + +if(lt->num_map_entries <= 256) { map_nbytes = 1; } +else if(lt->num_map_entries <= 256*256) { map_nbytes = 2; } +else if(lt->num_map_entries <= 256*256*256) { map_nbytes = 3; } +else { map_nbytes = 4; } + +if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256) { idx_nbytes = 1; } +else if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256*256) { idx_nbytes = 2; } +else if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256*256*256) { idx_nbytes = 3; } +else { idx_nbytes = 4; } + +if(using_partial) + { + /* skip */ + partial_length = 1 + /* lt->timepos */ + lt->timepos * sizeof(lxttime_t)+ /* timevals */ + + 1 + /* map_nbytes */ + (iter_hi-iter) * map_nbytes + /* actual map */ + 1; /* idx_nbytes */ + + for(j=iter;jsorted_facs[j]; + total_chgs += s->chgpos; + } + total_chgs *= idx_nbytes; /* vch skip */ + + partial_length += total_chgs; /* actual changes */ + + if(using_partial_zip) + { + fseeko(lt->handle, 0L, SEEK_END); + current_iter_pos = ftello(lt->handle); + lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ + lxt2_wr_emit_u32(lt, partial_length+9); /* size of this section (uncompressed) */ + lxt2_wr_emit_u32(lt, iter); /* begin iter of section */ + fflush(lt->handle); + + lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); + lt->zpackcount = 0; + } + + lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_TIME_PARTIAL); + lxt2_wr_emit_u32z(lt, iter); + lxt2_wr_emit_u32z(lt, partial_length); + } + else + { + lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_TIME); + } + +lxt2_wr_emit_u8z(lt, lt->timepos); +for(i=0;itimepos;i++) + { + lxt2_wr_emit_u64z(lt, (lt->timetable[i]>>32)&0xffffffff, lt->timetable[i]&0xffffffff); + } +gzflush_buffered(lt, 0); + + +lxt2_wr_emit_u8z(lt, map_nbytes); +for(j=iter;jsorted_facs[j]; + lt->mapdict = lxt2_wr_ds_splay (s->msk, lt->mapdict); + val = lt->mapdict->val; + + switch(map_nbytes) + { + case 1: lxt2_wr_emit_u8z(lt, val); break; + case 2: lxt2_wr_emit_u16z(lt, val); break; + case 3: lxt2_wr_emit_u24z(lt, val); break; + case 4: lxt2_wr_emit_u32z(lt, val); break; + } + + s->msk = LXT2_WR_GRAN_0VAL; + } + + +lxt2_wr_emit_u8z(lt, idx_nbytes); +gzflush_buffered(lt, 0); +for(j=iter;jsorted_facs[j]; + + for(i=0;ichgpos;i++) + { + switch(idx_nbytes) + { + case 1: lxt2_wr_emit_u8z (lt, s->chg[i]); break; + case 2: lxt2_wr_emit_u16z(lt, s->chg[i]); break; + case 3: lxt2_wr_emit_u24z(lt, s->chg[i]); break; + case 4: lxt2_wr_emit_u32z(lt, s->chg[i]); break; + } + } + + s->chgpos = 0; + } + +if(using_partial_zip) + { + off_t clen; + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + + clen = lt->position - current_iter_pos - 12; + fseeko(lt->handle, current_iter_pos, SEEK_SET); + + lt->zpackcount_cumulative+=lt->zpackcount; + + lxt2_wr_emit_u32(lt, clen); + } + else + { + gzflush_buffered(lt, 0); + } +} /* ...for(iter) */ + + +lt->timepos = 0; +lt->timegranule++; + +if(lt->break_size) + { + early_flush = (ftello(lt->handle) >= lt->break_size); + } + else + { + early_flush = 0; + } + +if((lt->timegranule>=lt->maxgranule)||(do_finalize)||(early_flush)) + { + off_t unclen, clen; + lxt2_wr_ds_Tree *dt, *dt2; + lxt2_wr_dslxt_Tree *ds, *ds2; + + if(using_partial_zip) + { + fseeko(lt->handle, 0L, SEEK_END); + current_iter_pos = ftello(lt->handle); + lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ + lxt2_wr_emit_u32(lt, 0); /* size of this section (uncompressed) */ + lxt2_wr_emit_u32(lt, ~0); /* control section */ + fflush(lt->handle); + + lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); + lt->zpackcount = 0; + } + + /* fprintf(stderr, "reached granule %d, finalizing block for section %d\n", lt->timegranule, lt->numsections); */ + lt->numsections++; + + /* finalize string dictionary */ + lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_DICT); + + ds = lt->dict_head; + /* fprintf(stderr, "num_dict_entries: %d\n", lt->num_dict_entries); */ + gzflush_buffered(lt, 0); + for(i=0;inum_dict_entries;i++) + { + /* fprintf(stderr, "%8d %8d) '%s'\n", ds->val, i, ds->item); */ + if(ds->val != i) + { + fprintf(stderr, "internal error line %d\n", __LINE__); + exit(255); + } + + lxt2_wr_emit_stringz(lt, ds->item); + ds2 = ds->next; + free(ds->item); + free(ds); + ds = ds2; + } + lt->dict_head = lt->dict_curr = lt->dict = NULL; + + /* finalize map dictionary */ + dt = lt->mapdict_head; + /* fprintf(stderr, "num_map_entries: %d\n", lt->num_map_entries); */ + gzflush_buffered(lt, 0); + for(i=0;inum_map_entries;i++) + { + /* fprintf(stderr, "+++ %08x (%d)(%d)\n", dt->item, i, dt->val); */ + if(((unsigned int)dt->val) != i) + { + fprintf(stderr, "internal error line %d\n", __LINE__); + exit(255); + } + +#if LXT2_WR_GRANULE_SIZE > 32 + lxt2_wr_emit_u64z(lt, (dt->item>>32)&0xffffffff, dt->item&0xffffffff); +#else + lxt2_wr_emit_u32z(lt, dt->item); +#endif + + dt2 = dt->next; + free(dt); + dt = dt2; + } + lt->mapdict_head = lt->mapdict_curr = lt->mapdict = NULL; + + lxt2_wr_emit_u32z(lt, lt->num_dict_entries); /* -12 */ + lxt2_wr_emit_u32z(lt, lt->dict_string_mem_required); /* -8 */ + lxt2_wr_emit_u32z(lt, lt->num_map_entries); /* -4 */ + + lt->num_map_entries = 0; + lt->num_dict_entries = lt->dict_string_mem_required = 0; + + /* fprintf(stderr, "returned from finalize..\n"); */ + + if(using_partial_zip) + { + off_t c_len; + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + + c_len = lt->position - current_iter_pos - 12; + fseeko(lt->handle, current_iter_pos, SEEK_SET); + + lt->zpackcount_cumulative+=lt->zpackcount; + lxt2_wr_emit_u32(lt, c_len); + lxt2_wr_emit_u32(lt, lt->zpackcount); + } + else + { + gzflush_buffered(lt, 1); + } + + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + /* fprintf(stderr, "file position after dumping dict: %d 0x%08x\n", lt->position, lt->position); */ + + unclen = lt->zpackcount; + clen = lt->position - lt->current_chunkz; + + /* fprintf(stderr, "%d/%d un/compressed bytes in section\n", unclen, clen); */ + + fseeko(lt->handle, lt->current_chunk, SEEK_SET); + if(using_partial_zip) + { + lxt2_wr_emit_u32(lt, lt->zpackcount_cumulative); + lxt2_wr_emit_u32(lt, clen); + } + else + { + lxt2_wr_emit_u32(lt, unclen); + lxt2_wr_emit_u32(lt, clen); + } + lxt2_wr_emit_u64(lt, (lt->firsttime>>32)&0xffffffff, lt->firsttime&0xffffffff); + lxt2_wr_emit_u64(lt, (lt->lasttime>>32)&0xffffffff, lt->lasttime&0xffffffff); + + /* fprintf(stderr, "start: %lld, end %lld\n", lt->firsttime, lt->lasttime); */ + + lt->timegranule=0; + lt->numblock++; + } + +if(do_finalize) + { + lt->flush_valid = 1; + lt->flushtime = lt->lasttime; + } +} + + +int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval) +{ +int rc=0; + +if(lt) + { + if(lt->timeset) + { + if(timeval > lt->maxtime) + { + if(lt->bumptime) + { + lt->bumptime = 0; + + if(!lt->flush_valid) + { + lt->timepos++; + } + else + { + lt->flush_valid = 0; + } + + if(lt->timepos == LXT2_WR_GRANULE_SIZE) + { + /* fprintf(stderr, "flushing granule to disk at time %d\n", (unsigned int)timeval); */ + lxt2_wr_flush_granule(lt, 0); + } + } + + /* fprintf(stderr, "updating time to %d (%d dict entries/%d bytes)\n", (unsigned int)timeval, lt->num_dict_entries, lt->dict_string_mem_required); */ + lt->timetable[lt->timepos] = timeval; + lt->lasttime = timeval; + } + } + else + { + lt->timeset = 1; + lt->mintime = lt->maxtime = timeval; + + lt->timetable[lt->timepos] = timeval; + } + + if( (!lt->timepos) && (!lt->timegranule) ) + { + lt->firsttime = timeval; + lt->lasttime = timeval; + } + + if( (!lt->timepos) && (!lt->timegranule) && ((!lt->numblock)||(!lt->no_checkpoint)) ) + { + /* fprintf(stderr, "initial value burst timepos==0, timegranule==0\n"); */ + if(lt->blackout) + { + lt->blackout = 0; + lxt2_wr_set_dumpoff(lt); + } + else + { + struct lxt2_wr_symbol *s = lt->symchain; + while(s) + { + if((!(s->flags&LXT2_WR_SYM_F_ALIAS))&&(s->rows<2)) + { + if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) + { + lxt2_wr_emit_value_bit_string(lt, s, 0, s->value); + } + else if (s->flags&LXT2_WR_SYM_F_DOUBLE) + { + double value = 0; + + sscanf(s->value, "%lg", &value); + errno = 0; + lxt2_wr_emit_value_double(lt, s, 0, value); + } + else if (s->flags&LXT2_WR_SYM_F_STRING) + { + lxt2_wr_emit_value_string(lt, s, 0, s->value); + } + } + s=s->symchain; + } + } + /* fprintf(stderr, "done initial value burst timepos==0, timegranule==0\n"); */ + } + + lt->granule_dirty = 1; + rc = 1; + } + +return(rc); +} + + +/* + * sets trace timescale as 10**x seconds + */ +void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale) +{ +if(lt) + { + lt->timescale = timescale; + } +} + + +/* + * set number of granules per section + * (can modify dynamically) + */ +void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule) +{ +if(lt) + { + if(!maxgranule) maxgranule = ~0; + lt->maxgranule = maxgranule; + } +} + + +/* + * Sets bracket stripping (useful for VCD conversions of + * bitblasted nets) + */ +void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit) +{ +if(lt) + { + lt->do_strip_brackets = (doit!=0); + } +} + + + +static char *lxt2_wr_expand_integer_to_bits(unsigned int len, int value) +{ +static char s[33]; +char *p = s; +unsigned int i; + +if(len>32) len=32; + +len--; + +for(i=0;i<=len;i++) + { + *(p++) = '0' | ((value & (1<<(len-i)))!=0); + } +*p = 0; + +return(s); +} + + +int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value) +{ +int rc=0; + +if((!lt)||(lt->blackout)||(!s)||(row)) return(rc); + +return(lxt2_wr_emit_value_bit_string(lt, s, row, lxt2_wr_expand_integer_to_bits(s->len, value))); +} + + + +int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value) +{ +int rc=0; + +if((!lt)||(lt->blackout)||(!s)||(row)) return(rc); + +if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if(s->flags&LXT2_WR_SYM_F_DOUBLE) + { + char d_buf[32]; + unsigned int idx; + + rc = 1; + sprintf(d_buf, "%.16g", value); + if(!strcmp(d_buf, s->value)) return(rc); + + lt->bumptime = 1; + free(s->value); + s->value = strdup(d_buf); + + lt->dict = lxt2_wr_dslxt_splay (s->value, lt->dict); + + if(!lxt2_wr_dslxt_success) + { + unsigned int vlen = strlen(d_buf)+1; + char *vcopy = (char *)malloc(vlen); + strcpy(vcopy, d_buf); + lt->dict_string_mem_required += vlen; + lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); + + if(lt->dict_curr) + { + lt->dict_curr->next = lt->dict; + lt->dict_curr = lt->dict; + } + else + { + lt->dict_head = lt->dict_curr = lt->dict; + } + + idx = lt->num_dict_entries + LXT2_WR_DICT_START; + lt->num_dict_entries++; + } + else + { + idx = lt->dict->val + LXT2_WR_DICT_START; + } + + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = idx; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = idx; + } + + lt->granule_dirty = 1; + } + +return(rc); +} + + +int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value) +{ +int rc=0; + +if((!lt)||(lt->blackout)||(!s)||(!value)||(row)) return(rc); + +if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if(s->flags&LXT2_WR_SYM_F_STRING) + { + unsigned int idx; + + rc = 1; + if(!strcmp(value, s->value)) return(rc); + + lt->bumptime = 1; + free(s->value); + s->value = strdup(value); + + lt->dict = lxt2_wr_dslxt_splay (s->value, lt->dict); + + if(!lxt2_wr_dslxt_success) + { + unsigned int vlen = strlen(value)+1; + char *vcopy = (char *)malloc(vlen); + strcpy(vcopy, value); + lt->dict_string_mem_required += vlen; + lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); + + if(lt->dict_curr) + { + lt->dict_curr->next = lt->dict; + lt->dict_curr = lt->dict; + } + else + { + lt->dict_head = lt->dict_curr = lt->dict; + } + + idx = lt->num_dict_entries + LXT2_WR_DICT_START; + lt->num_dict_entries++; + } + else + { + idx = lt->dict->val + LXT2_WR_DICT_START; + } + + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = idx; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = idx; + } + + lt->granule_dirty = 1; + } + +return(rc); +} + + +int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value) +{ +int rc=0; +char *vpnt; +char *vfix; +int valuelen; +int i; + +if((!lt)||(lt->blackout)||(!s)||(!value)||(!*value)||(row)) return(rc); + +if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +valuelen = strlen(value); /* ensure string is proper length */ +if(valuelen == s->len) + { + vfix = (char*)wave_alloca(s->len+1); + strcpy(vfix, value); + value = vfix; + } + else + { + vfix = (char*)wave_alloca(s->len+1); + + if(valuelen < s->len) + { + int lendelta = s->len - valuelen; + memset(vfix, (value[0]!='1') ? value[0] : '0', lendelta); + strcpy(vfix+lendelta, value); + } + else + { + memcpy(vfix, value, s->len); + vfix[s->len] = 0; + } + + value = vfix; + } + +for(i=0;ilen;i++) + { + unsigned char ch = value[i]; + if((ch>='A')&&(ch<='Z')) value[i] = ch + ('a'-'A'); + } + +if ( (lt->timepos || lt->timegranule) && !strcmp(value, s->value) ) + { + return(1); /* redundant value change */ + } + + +if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) + { + char prevch; + int idx; + + lt->bumptime = 1; + + vpnt = value; + prevch = *vpnt; + while(*vpnt) + { + if(prevch == *vpnt) + { + vpnt++; + } + else + { + prevch = 0; + break; + } + } + + switch(prevch) + { + case '0': idx = LXT2_WR_ENC_0; break; + case '1': idx = LXT2_WR_ENC_1; break; + case 'X': + case 'x': idx = LXT2_WR_ENC_X; break; + case 'Z': + case 'z': idx = LXT2_WR_ENC_Z; break; + default: idx = -1; break; + } + + if((lt->timepos)||(lt->timegranule)) + { + for(i=0;ilen;i++) + { + char ch = value[i]; + + switch(ch) + { + case '0': if(s->value[i]!='1') goto nextalg; else break; + case '1': if(s->value[i]!='0') goto nextalg; else break; + default: goto nextalg; + } + } + idx = LXT2_WR_ENC_INV; goto do_enc; + + nextalg: + if(s->len > 1) + { + if(!memcmp(s->value+1, value, s->len-1)) + { + if((value[s->len-1]&0xfe)=='0') + { + idx = LXT2_WR_ENC_LSH0 + (value[s->len-1]&0x01); + goto do_enc; + } + } + else + if(!memcmp(s->value, value+1, s->len-1)) + { + if((value[0]&0xfe)=='0') + { + idx = LXT2_WR_ENC_RSH0 + (value[0]&0x01); + goto do_enc; + } + } + + if(s->len <= 32) + { + unsigned int intval_old = 0, intval_new = 0; + unsigned int msk; + + for(i=0;ilen;i++) + { + char ch = value[i]; + if((ch!='0')&&(ch!='1')) goto idxchk; + intval_new <<= 1; + intval_new |= ((unsigned int)(ch&1)); + + ch = s->value[i]; + if((ch!='0')&&(ch!='1')) goto idxchk; + intval_old <<= 1; + intval_old |= ((unsigned int)(ch&1)); + } + + msk = (~0)>>(32-s->len); + if( ((intval_old+1)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD1; goto do_enc; } + if( ((intval_old-1)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB1; goto do_enc; } + + if( ((intval_old+2)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD2; goto do_enc; } + if( ((intval_old-2)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB2; goto do_enc; } + + if( ((intval_old+3)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD3; goto do_enc; } + if( ((intval_old-3)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB3; goto do_enc; } + + if(s->len > 2) + { + if( ((intval_old+4)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD4; goto do_enc; } + if( ((intval_old-4)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB4; goto do_enc; } + } + + } + } + } + + +idxchk: if(idx<0) + { + vpnt = lxt2_wr_vcd_truncate_bitvec(value); + lt->dict = lxt2_wr_dslxt_splay (vpnt, lt->dict); + + if(!lxt2_wr_dslxt_success) + { + unsigned int vlen = strlen(vpnt)+1; + char *vcopy = (char *)malloc(vlen); + strcpy(vcopy, vpnt); + lt->dict_string_mem_required += vlen; + lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); + + if(lt->dict_curr) + { + lt->dict_curr->next = lt->dict; + lt->dict_curr = lt->dict; + } + else + { + lt->dict_head = lt->dict_curr = lt->dict; + } + + idx = lt->num_dict_entries + LXT2_WR_DICT_START; + lt->num_dict_entries++; + } + else + { + idx = lt->dict->val + LXT2_WR_DICT_START; + } + } + +do_enc: + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = idx; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = idx; + } + + strncpy(s->value, value, s->len); + + lt->granule_dirty = 1; + } + +return(rc); +} + + +/* + * dumping control + */ +void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt) +{ +struct lxt2_wr_symbol *s; + +if((lt)&&(!lt->blackout)) + { + if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + + s = lt->symchain; + while(s) + { + if(!(s->flags&LXT2_WR_SYM_F_ALIAS)) + { + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = LXT2_WR_ENC_BLACKOUT; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = LXT2_WR_ENC_BLACKOUT; + } + } + + s=s->symchain; + } + + lt->bumptime = 1; + lt->blackout = 1; + lt->granule_dirty = 1; + } +} + + +void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt) +{ +int i; +struct lxt2_wr_symbol *s; + +if((lt)&&(lt->blackout)) + { + lt->blackout = 0; + + s = lt->symchain; + while(s) + { + if(!(s->flags&LXT2_WR_SYM_F_ALIAS)) + { + if(s->flags&LXT2_WR_SYM_F_DOUBLE) + { + free(s->value); + s->value = strdup("0"); /* will cause mismatch then flush */ + } + else + { + if(!(s->flags&LXT2_WR_SYM_F_STRING)) + { + s->value[0] = '-'; /* will cause mismatch then flush */ + for(i=1;ilen;i++) + { + s->value[i] = 'x'; /* initial value */ + } + s->value[i]=0; + } + else + { + free(s->value); + s->value = (char*)calloc(1, 1*sizeof(char)); + } + } + } + + s=s->symchain; + } + + s = lt->symchain; + while(s) + { + if((!(s->flags&LXT2_WR_SYM_F_ALIAS))&&(s->rows<2)) + { + char tmp[16]; // To get rid of the warning + if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) + { + strcpy(tmp, "x"); + lxt2_wr_emit_value_bit_string(lt, s, 0, tmp); + } + else if (s->flags&LXT2_WR_SYM_F_DOUBLE) + { + double value; + sscanf("NaN", "%lg", &value); + lxt2_wr_emit_value_double(lt, s, 0, value); + } + else if (s->flags&LXT2_WR_SYM_F_STRING) + { + strcpy(tmp, "UNDEF"); + lxt2_wr_emit_value_string(lt, s, 0, tmp); + } + } + s=s->symchain; + } + } +} + + +/* + * flush the trace... + */ +void lxt2_wr_flush(struct lxt2_wr_trace *lt) +{ +if(lt) + { + if((lt->timegranule)||(lt->timepos > 0)) + { + if(lt->granule_dirty) + { + lt->timepos++; + lxt2_wr_flush_granule(lt, 1); + } + } + } +} + + +/* + * close out the trace and fixate it + */ +void lxt2_wr_close(struct lxt2_wr_trace *lt) +{ +if(lt) + { + if(lt->granule_dirty) + { + lt->timepos++; + lxt2_wr_flush_granule(lt, 1); + } + + if(lt->symchain) + { + struct lxt2_wr_symbol *s = lt->symchain; + struct lxt2_wr_symbol *s2; + + while(s) + { + free(s->name); + free(s->value); + s2=s->symchain; + free(s); + s=s2; + } + + lt->symchain=NULL; + } + + free(lt->lxtname); + free(lt->sorted_facs); + fclose(lt->handle); + free(lt); + } + +} + +/* + * set compression depth + */ +void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth) +{ +if(lt) + { + if(depth > 9) depth = 9; + sprintf(lt->zmode, "wb%u", depth); + } +} + + +/* + * time zero offset + */ +void lxt2_wr_set_timezero(struct lxt2_wr_trace *lt, lxtstime_t timeval) +{ +if(lt) + { + lt->timezero = timeval; + } +} diff --git a/include/lxt2/lxt2_write.h b/include/lxt2/lxt2_write.h new file mode 100644 index 000000000..5e3ffb480 --- /dev/null +++ b/include/lxt2/lxt2_write.h @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2003-2012 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef DEFS_LXTW_H +#define DEFS_LXTW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_FSEEKO +#define fseeko fseek +#define ftello ftell +#endif + +#include "wavealloca.h" + +#define LXT2_WR_HDRID (0x1380) +#define LXT2_WR_VERSION (0x0001) + +#define LXT2_WR_GRANULE_SIZE (64) +#define LXT2_WR_GRANULE_NUM (256) +#define LXT2_WR_PARTIAL_SIZE (2048) + +#define LXT2_WR_GRAN_SECT_TIME 0 +#define LXT2_WR_GRAN_SECT_DICT 1 +#define LXT2_WR_GRAN_SECT_TIME_PARTIAL 2 + +#define LXT2_WR_GZWRITE_BUFFER 4096 +#define LXT2_WR_SYMPRIME 500009 + +typedef uint64_t lxttime_t; +typedef int64_t lxtstime_t; + + +#ifndef _MSC_VER + #ifdef __MINGW32__ + #define LXT2_WR_LLD "%I64d" + #else + #define LXT2_WR_LLD "%lld" + #endif + #define LXT2_WR_LLDESC(x) x##LL + #define LXT2_WR_ULLDESC(x) x##ULL +#else + #define LXT2_WR_LLD "%I64d" + #define LXT2_WR_LLDESC(x) x##i64 + #define LXT2_WR_ULLDESC(x) x##i64 +#endif + +#if LXT2_WR_GRANULE_SIZE > 32 +typedef unsigned long long granmsk_t; +#define LXT2_WR_GRAN_0VAL (LXT2_WR_ULLDESC(0)) +#define LXT2_WR_GRAN_1VAL (LXT2_WR_ULLDESC(1)) +#else +typedef unsigned int granmsk_t; +#define LXT2_WR_GRAN_0VAL (0) +#define LXT2_WR_GRAN_1VAL (1) +#endif + + +enum LXT2_WR_Encodings { + LXT2_WR_ENC_0, + LXT2_WR_ENC_1, + LXT2_WR_ENC_INV, + LXT2_WR_ENC_LSH0, + LXT2_WR_ENC_LSH1, + LXT2_WR_ENC_RSH0, + LXT2_WR_ENC_RSH1, + + LXT2_WR_ENC_ADD1, + LXT2_WR_ENC_ADD2, + LXT2_WR_ENC_ADD3, + LXT2_WR_ENC_ADD4, + + LXT2_WR_ENC_SUB1, + LXT2_WR_ENC_SUB2, + LXT2_WR_ENC_SUB3, + LXT2_WR_ENC_SUB4, + + LXT2_WR_ENC_X, + LXT2_WR_ENC_Z, + + LXT2_WR_ENC_BLACKOUT, + + LXT2_WR_DICT_START + }; + +/* + * integer splay + */ +typedef struct lxt2_wr_ds_tree_node lxt2_wr_ds_Tree; +struct lxt2_wr_ds_tree_node { + lxt2_wr_ds_Tree * left, * right; + granmsk_t item; + int val; + lxt2_wr_ds_Tree * next; +}; + + +/* + * string splay + */ +typedef struct lxt2_wr_dslxt_tree_node lxt2_wr_dslxt_Tree; +struct lxt2_wr_dslxt_tree_node { + lxt2_wr_dslxt_Tree * left, * right; + char *item; + unsigned int val; + lxt2_wr_dslxt_Tree * next; +}; + + +struct lxt2_wr_trace +{ +FILE *handle; +gzFile zhandle; + +lxt2_wr_dslxt_Tree *dict; /* dictionary manipulation */ +unsigned int num_dict_entries; +unsigned int dict_string_mem_required; +lxt2_wr_dslxt_Tree *dict_head; +lxt2_wr_dslxt_Tree *dict_curr; + +lxt2_wr_ds_Tree *mapdict; /* bitmap compression */ +unsigned int num_map_entries; +lxt2_wr_ds_Tree *mapdict_head; +lxt2_wr_ds_Tree *mapdict_curr; + +off_t position; +off_t zfacname_predec_size, zfacname_size, zfacgeometry_size; +off_t zpackcount, zpackcount_cumulative; +off_t current_chunk, current_chunkz; + +struct lxt2_wr_symbol *sym[LXT2_WR_SYMPRIME]; +struct lxt2_wr_symbol **sorted_facs; +struct lxt2_wr_symbol *symchain; +unsigned int numfacs, numalias; +int numfacbytes; +int longestname; + +int numsections, numblock; +off_t facname_offset, facgeometry_offset; + +lxttime_t mintime, maxtime; +lxtstime_t timezero; +unsigned int timegranule; +int timescale; +unsigned int timepos; +unsigned int maxgranule; +lxttime_t firsttime, lasttime; +lxttime_t timetable[LXT2_WR_GRANULE_SIZE]; + +unsigned int partial_iter; + +char *compress_fac_str; +int compress_fac_len; + +lxttime_t flushtime; +unsigned flush_valid : 1; + +unsigned do_strip_brackets : 1; +unsigned emitted : 1; /* gate off change field zmode changes when set */ +unsigned timeset : 1; /* time has been modified from 0..0 */ +unsigned bumptime : 1; /* says that must go to next time position in granule as value change exists for current time */ +unsigned granule_dirty : 1; /* for flushing out final block */ +unsigned blackout : 1; /* blackout on/off */ +unsigned partial : 1; /* partial (vertical) trace support */ +unsigned partial_zip : 1; /* partial (vertical) trace support for zip subregions */ +unsigned no_checkpoint : 1; /* turns off interblock checkpointing */ +unsigned partial_preference : 1; /* partial preference encountered on some facs */ + +char initial_value; + +char zmode[4]; /* fills in with "wb0".."wb9" */ +unsigned int gzbufpnt; +unsigned char gzdest[LXT2_WR_GZWRITE_BUFFER + 4]; /* enough for zlib buffering */ + +char *lxtname; +off_t break_size; +off_t break_header_size; +unsigned int break_number; +}; + + +struct lxt2_wr_symbol +{ +struct lxt2_wr_symbol *next; +struct lxt2_wr_symbol *symchain; +char *name; +int namlen; + +int facnum; +struct lxt2_wr_symbol *aliased_to; + +char *value; /* fac's actual value */ + +unsigned int rows; +int msb, lsb; +int len; +int flags; + +unsigned partial_preference : 1; /* in order to shove nets to the first partial group */ + +unsigned int chgpos; +granmsk_t msk; /* must contain LXT2_WR_GRANULE_SIZE bits! */ +unsigned int chg[LXT2_WR_GRANULE_SIZE]; +}; + + +#define LXT2_WR_SYM_F_BITS (0) +#define LXT2_WR_SYM_F_INTEGER (1<<0) +#define LXT2_WR_SYM_F_DOUBLE (1<<1) +#define LXT2_WR_SYM_F_STRING (1<<2) +#define LXT2_WR_SYM_F_TIME (LXT2_WR_SYM_F_STRING) /* user must correctly format this as a string */ +#define LXT2_WR_SYM_F_ALIAS (1<<3) + +#define LXT2_WR_SYM_F_SIGNED (1<<4) +#define LXT2_WR_SYM_F_BOOLEAN (1<<5) +#define LXT2_WR_SYM_F_NATURAL ((1<<6)|(LXT2_WR_SYM_F_INTEGER)) +#define LXT2_WR_SYM_F_POSITIVE ((1<<7)|(LXT2_WR_SYM_F_INTEGER)) +#define LXT2_WR_SYM_F_CHARACTER (1<<8) + +#define LXT2_WR_SYM_F_CONSTANT (1<<9) +#define LXT2_WR_SYM_F_VARIABLE (1<<10) +#define LXT2_WR_SYM_F_SIGNAL (1<<11) + +#define LXT2_WR_SYM_F_IN (1<<12) +#define LXT2_WR_SYM_F_OUT (1<<13) +#define LXT2_WR_SYM_F_INOUT (1<<14) + +#define LXT2_WR_SYM_F_WIRE (1<<15) +#define LXT2_WR_SYM_F_REG (1<<16) + + + /* file I/O */ +struct lxt2_wr_trace * lxt2_wr_init(const char *name); +void lxt2_wr_flush(struct lxt2_wr_trace *lt); +void lxt2_wr_close(struct lxt2_wr_trace *lt); + + /* for dealing with very large traces, split into multiple files approximately "siz" in length */ +void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz); + + /* 0 = no compression, 9 = best compression, 4 = default */ +void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth); + + /* default is partial off, turning on makes for faster trace reads, nonzero zipmode causes vertical compression */ +void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt); +void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode); +void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name); + + /* turning off checkpointing makes for smaller files */ +void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt); +void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt); + + /* facility creation */ +void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value); +struct lxt2_wr_symbol * lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name); +struct lxt2_wr_symbol * lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags); +struct lxt2_wr_symbol * lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb); +void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit); + + /* each granule is LXT2_WR_GRANULE_SIZE (32 or 64) timesteps, default is 256 per section */ +void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule); + + /* time ops */ +void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale); +void lxt2_wr_set_timezero(struct lxt2_wr_trace *lt, lxtstime_t timeval); +int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval); +int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval); +int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval); +int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval); + + /* allows blackout regions in LXT files */ +void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt); +void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt); + + /* left fill on bit_string uses vcd semantics (left fill with value[0] unless value[0]=='1', then use '0') */ +int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value); +int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value); +int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); +int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/lxt2/wavealloca.h b/include/lxt2/wavealloca.h new file mode 100644 index 000000000..4be2e4937 --- /dev/null +++ b/include/lxt2/wavealloca.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Tony Bybell 1999. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef WAVE_ALLOCA_H +#define WAVE_ALLOCA_H +#include +#ifdef HAVE_ALLOCA_H +#include +#elif defined(__GNUC__) +#ifndef __MINGW32__ +#ifndef alloca +#define alloca __builtin_alloca +#endif +#else +#include +#endif +#elif defined(_MSC_VER) +#include +#define alloca _alloca +#endif +#define wave_alloca alloca +#endif + diff --git a/include/verilated.h b/include/verilated.h index 45ec64d10..f25a10b1f 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -79,6 +79,8 @@ class VerilatedVar; class VerilatedVarNameMap; class VerilatedVcd; class VerilatedVcdC; +class VerilatedLxt2; +class VerilatedLxt2C; enum VerilatedVarType { VLVT_UNKNOWN=0, diff --git a/include/verilated_lxt2_c.cpp b/include/verilated_lxt2_c.cpp new file mode 100644 index 000000000..1f6b5d748 --- /dev/null +++ b/include/verilated_lxt2_c.cpp @@ -0,0 +1,180 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// THIS MODULE IS PUBLICLY LICENSED +// +// Copyright 2001-2018 by Wilson Snyder. This program is free software; +// you can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License Version 2.0. +// +// This is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +//============================================================================= +/// +/// \file +/// \brief C++ Tracing in LXT2 Format +/// +//============================================================================= +// SPDIFF_OFF + +#include "verilatedos.h" +#include "verilated.h" +#include "verilated_lxt2_c.h" +// Include the GTKWave implementation directly +#include "lxt2/lxt2_write.cpp" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# include +#else +# include +#endif + +//============================================================================= + +class VerilatedLxt2CallInfo { +protected: + friend class VerilatedLxt2; + VerilatedLxt2Callback_t m_initcb; ///< Initialization Callback function + VerilatedLxt2Callback_t m_fullcb; ///< Full Dumping Callback function + VerilatedLxt2Callback_t m_changecb; ///< Incremental Dumping Callback function + void* m_userthis; ///< Fake "this" for caller + vluint32_t m_code; ///< Starting code number + // CONSTRUCTORS + VerilatedLxt2CallInfo (VerilatedLxt2Callback_t icb, VerilatedLxt2Callback_t fcb, + VerilatedLxt2Callback_t changecb, + void* ut, vluint32_t code) + : m_initcb(icb), m_fullcb(fcb), m_changecb(changecb), m_userthis(ut), m_code(code) {}; + ~VerilatedLxt2CallInfo() {} +}; + +//============================================================================= +// VerilatedLxt2 + +VerilatedLxt2::VerilatedLxt2(lxt2_wr_trace* lxt2) + : m_lxt2(lxt2), + m_fullDump(true), + m_scopeEscape('.') {} + +void VerilatedLxt2::open(const char* filename) VL_MT_UNSAFE { + m_assertOne.check(); + m_lxt2 = lxt2_wr_init(filename); + for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) { + VerilatedLxt2CallInfo* cip = m_callbacks[ent]; + cip->m_code = 1; + (cip->m_initcb)(this, cip->m_userthis, cip->m_code); + } +} + +void VerilatedLxt2::module(const std::string& name) { + m_module = name; +} + +//============================================================================= +// Decl + +void VerilatedLxt2::declSymbol(vluint32_t code, const char* name, int arraynum, int msb, int lsb, int flags) { + if (msb == 0 && lsb == 0) { + msb = lsb = -1; + } + std::pair p + = m_code2symbol.insert(std::make_pair(code, (lxt2_wr_symbol*)(NULL))); + std::stringstream name_ss; + name_ss <= 0) { + name_ss <<"("<second = lxt2_wr_symbol_add(m_lxt2, name_s.c_str(), 0, msb, lsb, flags); + assert(p.first->second); + } else { // Alias + lxt2_wr_symbol_alias(m_lxt2, p.first->second->name, name_s.c_str(), msb, lsb); + } +} + +//============================================================================= +// Callbacks + +void VerilatedLxt2::addCallback( + VerilatedLxt2Callback_t initcb, VerilatedLxt2Callback_t fullcb, + VerilatedLxt2Callback_t changecb, void* userthis) VL_MT_UNSAFE_ONE { + m_assertOne.check(); + if (VL_UNLIKELY(isOpen())) { + std::string msg = std::string("Internal: ")+__FILE__+"::"+__FUNCTION__+" called with already open file"; + VL_FATAL_MT(__FILE__,__LINE__,"",msg.c_str()); + } + VerilatedLxt2CallInfo* vci = new VerilatedLxt2CallInfo(initcb, fullcb, changecb, userthis, 1); + m_callbacks.push_back(vci); +} + +//============================================================================= +// Dumping + +void VerilatedLxt2::dump(vluint64_t timeui) { + if (!isOpen()) return; + if (VL_UNLIKELY(m_fullDump)) { + m_fullDump = false; // No need for more full dumps + for (vluint32_t ent = 0; ent< m_callbacks.size(); ent++) { + VerilatedLxt2CallInfo* cip = m_callbacks[ent]; + (cip->m_fullcb)(this, cip->m_userthis, cip->m_code); + } + return; + } + lxt2_wr_set_time64(m_lxt2, timeui); + for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) { + VerilatedLxt2CallInfo* cip = m_callbacks[ent]; + (cip->m_changecb)(this, cip->m_userthis, cip->m_code); + } +} + +//============================================================================= +// Helpers + +char* VerilatedLxt2::quad2Str(vluint64_t newval, int bits) { + m_valueStrBuffer.resize(bits+1); + char* s = m_valueStrBuffer.data(); + for (int i = 0; i < bits; ++i) { + *s = '0' + ((newval>>(bits-i-1))&1); + ++s; + } + *s = '\0'; + return m_valueStrBuffer.data(); +} + +char* VerilatedLxt2::array2Str(const vluint32_t* newval, int bits) { + int bq = bits/32, br = bits%32; + m_valueStrBuffer.resize(bits+1); + char* s = m_valueStrBuffer.data(); + for (int w = bq-1; w >= 0; --w) { + for (int i = 0; i < 32; ++i) { + *s = '0' + ((newval[w]>>(32-i-1))&1); + ++s; + } + } + for (int i = 0; i < br; ++i) { + *s = '0' + ((newval[bq]>>(br-i-1))&1); + ++s; + } + *s = '\0'; + return m_valueStrBuffer.data(); +} + +//******************************************************************** +// Local Variables: +// End: diff --git a/include/verilated_lxt2_c.h b/include/verilated_lxt2_c.h new file mode 100644 index 000000000..abac61b3f --- /dev/null +++ b/include/verilated_lxt2_c.h @@ -0,0 +1,204 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// THIS MODULE IS PUBLICLY LICENSED +// +// Copyright 2001-2018 by Wilson Snyder. This program is free software; +// you can redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License Version 2.0. +// +// This is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +//============================================================================= +/// +/// \file +/// \brief C++ Tracing in LXT2 Format +/// +//============================================================================= +// SPDIFF_OFF + +#ifndef _VERILATED_LXT2_C_H_ +#define _VERILATED_LXT2_C_H_ 1 + +#include "verilatedos.h" +#include "verilated.h" +#include "lxt2/lxt2_write.h" + +#include +#include +#include + +class VerilatedLxt2; +class VerilatedLxt2CallInfo; +typedef void (*VerilatedLxt2Callback_t)(VerilatedLxt2* vcdp, void* userthis, vluint32_t code); + +//============================================================================= +// VerilatedLxt2 +/// Base class to create a Verilator LXT2 dump +/// This is an internally used class - see VerilatedLxt2C for what to call from applications + +class VerilatedLxt2 { + typedef std::map Code2SymbolType; + typedef std::vector CallbackVec; +private: + lxt2_wr_trace* m_lxt2; + VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread + bool m_fullDump; + char m_scopeEscape; + std::string m_module; + CallbackVec m_callbacks; ///< Routines to perform dumping + Code2SymbolType m_code2symbol; + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedLxt2); + void declSymbol(vluint32_t code, const char* name, int arraynum, int msb, int lsb, int flags); + // helpers + std::vector m_valueStrBuffer; + char* quad2Str(vluint64_t newval, int bits); + char* array2Str(const vluint32_t *newval, int bits); +public: + explicit VerilatedLxt2(lxt2_wr_trace* lxt2=NULL); + ~VerilatedLxt2() { if (m_lxt2 == NULL) { lxt2_wr_close(m_lxt2); } } + bool isOpen() const { return m_lxt2 != NULL; } + void open(const char* filename) VL_MT_UNSAFE; + void flush() VL_MT_UNSAFE { lxt2_wr_flush(m_lxt2); } + void close() VL_MT_UNSAFE { + m_assertOne.check(); + lxt2_wr_close(m_lxt2); + m_lxt2 = NULL; + } + // void set_time_unit(const char* unit); ///< Set time units (s/ms, defaults to ns) + // void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); } + + // void set_time_resolution(const char* unit); ///< Set time resolution (s/ms, defaults to ns) + // void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); } + + // double timescaleToDouble(const char* unitp); + // std::string doubleToTimescale(double value); + + /// Change character that splits scopes. Note whitespace are ALWAYS escapes. + void scopeEscape(char flag) { m_scopeEscape = flag; } + /// Is this an escape? + bool isScopeEscape(char c) { return isspace(c) || c==m_scopeEscape; } + /// Inside dumping routines, called each cycle to make the dump + void dump(vluint64_t timeui); + /// Inside dumping routines, declare callbacks for tracings + void addCallback(VerilatedLxt2Callback_t init, VerilatedLxt2Callback_t full, + VerilatedLxt2Callback_t change, + void* userthis) VL_MT_UNSAFE_ONE; + + /// Inside dumping routines, declare a module + void module(const std::string& name); + /// Inside dumping routines, declare a signal + void declBit(vluint32_t code, const char* name, int arraynum) { + this->declSymbol(code, name, arraynum, 0, 0, LXT2_WR_SYM_F_BITS); + } + void declBus(vluint32_t code, const char* name, int arraynum, int msb, int lsb) { + this->declSymbol(code, name, arraynum, msb, lsb, LXT2_WR_SYM_F_BITS); + } + void declDouble(vluint32_t code, const char* name, int arraynum) { + this->declSymbol(code, name, arraynum, 63, 0, LXT2_WR_SYM_F_DOUBLE); + } + void declFloat(vluint32_t code, const char* name, int arraynum) { + this->declSymbol(code, name, arraynum, 63, 0, LXT2_WR_SYM_F_DOUBLE); + } + void declQuad(vluint32_t code, const char* name, int arraynum, int msb, int lsb) { + this->declSymbol(code, name, arraynum, msb, lsb, LXT2_WR_SYM_F_BITS); + } + void declArray(vluint32_t code, const char* name, int arraynum, int msb, int lsb) { + this->declSymbol(code, name, arraynum, msb, lsb, LXT2_WR_SYM_F_BITS); + } + + /// Inside dumping routines, dump one signal if it has changed + void chgBit(vluint32_t code, const vluint32_t newval) { + lxt2_wr_emit_value_int(m_lxt2, m_code2symbol[code], 0, newval); + } + void chgBus(vluint32_t code, const vluint32_t newval, int bits) { + lxt2_wr_emit_value_int(m_lxt2, m_code2symbol[code], 0, newval); + } + void chgDouble(vluint32_t code, const double newval) { + lxt2_wr_emit_value_double(m_lxt2, m_code2symbol[code], 0, newval); + } + void chgFloat(vluint32_t code, const float newval) { + lxt2_wr_emit_value_double(m_lxt2, m_code2symbol[code], 0, newval); + } + void chgQuad(vluint32_t code, const vluint64_t newval, int bits) { + lxt2_wr_emit_value_bit_string(m_lxt2, m_code2symbol[code], 0, quad2Str(newval, bits)); + } + void chgArray(vluint32_t code, const vluint32_t* newval, int bits) { + lxt2_wr_emit_value_bit_string(m_lxt2, m_code2symbol[code], 0, array2Str(newval, bits)); + } + + void fullBit(vluint32_t code, const vluint32_t newval) { chgBit(code, newval); } + void fullBus(vluint32_t code, const vluint32_t newval, int bits) { chgBus(code, newval, bits); } + void fullDouble(vluint32_t code, const double newval) { chgDouble(code, newval); } + void fullFloat(vluint32_t code, const float newval) { chgFloat(code, newval); } + void fullQuad(vluint32_t code, const vluint64_t newval, int bits) { chgQuad(code, newval, bits); } + void fullArray(vluint32_t code, const vluint32_t* newval, int bits) { chgArray(code, newval, bits); } + + void declTriBit (vluint32_t code, const char* name, int arraynum); + void declTriBus (vluint32_t code, const char* name, int arraynum, int msb, int lsb); + void declTriQuad (vluint32_t code, const char* name, int arraynum, int msb, int lsb); + void declTriArray (vluint32_t code, const char* name, int arraynum, int msb, int lsb); + void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri); + void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits); + void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits); + void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits); + void fullBitX(vluint32_t code); + void fullBusX(vluint32_t code, int bits); + void fullQuadX(vluint32_t code, int bits); + void fullArrayX(vluint32_t code, int bits); + void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri); + void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits); + void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits); + void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits); +}; + +//============================================================================= +// VerilatedLxt2C +/// Create a LXT2 dump file in C standalone (no SystemC) simulations. +/// Also derived for use in SystemC simulations. +/// Thread safety: Unless otherwise indicated, every function is VL_MT_UNSAFE_ONE + +class VerilatedLxt2C { + VerilatedLxt2 m_sptrace; ///< Trace file being created + + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedLxt2C); +public: + explicit VerilatedLxt2C(lxt2_wr_trace* filep=NULL) : m_sptrace(filep) {} + ~VerilatedLxt2C() {} +public: + // ACCESSORS + /// Is file open? + bool isOpen() const { return m_sptrace.isOpen(); } + // METHODS + /// Open a new LXT2 file + void open(const char* filename) VL_MT_UNSAFE_ONE { m_sptrace.open(filename); } + /// Close dump + void close() VL_MT_UNSAFE_ONE { m_sptrace.close(); } + /// Flush dump + void flush() VL_MT_UNSAFE_ONE { m_sptrace.flush(); } + /// Write one cycle of dump data + void dump(vluint64_t timeui) { m_sptrace.dump(timeui); } + /// Write one cycle of dump data - backward compatible and to reduce + /// conversion warnings. It's better to use a vluint64_t time instead. + void dump(double timestamp) { dump(static_cast(timestamp)); } + void dump(vluint32_t timestamp) { dump(static_cast(timestamp)); } + void dump(int timestamp) { dump(static_cast(timestamp)); } + /// Set time units (s/ms, defaults to ns) + /// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h + void set_time_unit(const char* unit) { /* TODO */ } + void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); } + /// Set time resolution (s/ms, defaults to ns) + /// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h + void set_time_resolution(const char* unit) { /* TODO */ } + void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); } + + /// Internal class access + inline VerilatedLxt2* spTrace() { return &m_sptrace; }; +}; + +#endif // guard diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index c2cd76009..f478553d4 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -2539,7 +2539,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) { } if (v3Global.opt.trace()) { if (modp->isTop()) puts("/// Trace signals in the model; called by application code\n"); - puts("void trace(VerilatedVcdC* tfp, int levels, int options=0);\n"); + puts("void trace("+v3Global.opt.traceClassBase()+"C* tfp, int levels, int options=0);\n"); if (modp->isTop() && optSystemC()) { puts("/// SC tracing; avoid overloaded virtual function lint warning\n"); puts("virtual void trace(sc_trace_file* tfp) const { ::sc_core::sc_module::trace(tfp); }\n"); @@ -2739,7 +2739,7 @@ class EmitCTrace : EmitCStmts { void emitTraceHeader() { // Includes - puts("#include \"verilated_vcd_c.h\"\n"); + puts("#include \""+v3Global.opt.traceSourceName()+"_c.h\"\n"); puts("#include \""+ symClassName() +".h\"\n"); puts("\n"); } @@ -2748,7 +2748,7 @@ class EmitCTrace : EmitCStmts { puts("\n//======================\n\n"); puts("void "+topClassName()+"::trace("); - puts("VerilatedVcdC* tfp, int, int) {\n"); + puts(v3Global.opt.traceClassBase()+"C* tfp, int, int) {\n"); puts( "tfp->spTrace()->addCallback(" "&"+topClassName()+"::traceInit" +", &"+topClassName()+"::traceFull" diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index a3ba685be..8e08d5cc5 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -89,9 +89,13 @@ public: putMakeClassEntry(of, "verilated_cov.cpp"); } if (v3Global.opt.trace()) { - putMakeClassEntry(of, "verilated_vcd_c.cpp"); + putMakeClassEntry(of, v3Global.opt.traceSourceName()+"_c.cpp"); if (v3Global.opt.systemC()) { - putMakeClassEntry(of, "verilated_vcd_sc.cpp"); + if (v3Global.opt.traceFormat() != TraceFormat::VCD) { + v3error("Unsupported: This trace format is not supported in SystemC, use VCD format."); + } else { + putMakeClassEntry(of, v3Global.opt.traceSourceName()+"_sc.cpp"); + } } } if (v3Global.opt.mtasks()) { diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 4f26ea739..5221fc044 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -695,6 +695,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char else if ( !strcmp (sw, "-sv") ) { m_defaultLanguage = V3LangCode::L1800_2005; } else if ( onoff (sw, "-threads-coarsen", flag/*ref*/)) { m_threadsCoarsen = flag; } // Undocumented, debug else if ( onoff (sw, "-trace", flag/*ref*/) ) { m_trace = flag; } + else if ( onoff (sw, "-trace-lxt2", flag/*ref*/) ) { m_trace = flag; m_traceFormat = TraceFormat::LXT2; addLdLibs("-lz"); } else if ( onoff (sw, "-trace-dups", flag/*ref*/) ) { m_traceDups = flag; } else if ( onoff (sw, "-trace-params", flag/*ref*/) ) { m_traceParams = flag; } else if ( onoff (sw, "-trace-structs", flag/*ref*/) ) { m_traceStructs = flag; } @@ -1277,6 +1278,7 @@ V3Options::V3Options() { m_threadsCoarsen = true; m_threadsMaxMTasks = 0; m_trace = false; + m_traceFormat = TraceFormat::VCD; m_traceDups = false; m_traceParams = true; m_traceStructs = false; diff --git a/src/V3Options.h b/src/V3Options.h index d3a182b5f..e0a2d0ae9 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -31,16 +31,49 @@ #include "V3Global.h" #include "V3LangCode.h" -//###################################################################### -// V3Options - Command line options - class V3OptionsImp; class FileLine; +//###################################################################### + +class TraceFormat { +public: + enum en { + VCD = 0, + LXT2 + } m_e; + inline TraceFormat(en _e = VCD) : m_e(_e) {} + explicit inline TraceFormat(int _e) : m_e(static_cast(_e)) {} + operator en() const { return m_e; } + string classBase() const { + static const char* const names[] = { + "VerilatedVcd", + "VerilatedLxt2" + }; + return names[m_e]; + } + string sourceName() const { + static const char* const names[] = { + "verilated_vcd", + "verilated_lxt2" + }; + return names[m_e]; + } +}; +inline bool operator==(TraceFormat lhs, TraceFormat rhs) { return (lhs.m_e == rhs.m_e); } +inline bool operator==(TraceFormat lhs, TraceFormat::en rhs) { return (lhs.m_e == rhs); } +inline bool operator==(TraceFormat::en lhs, TraceFormat rhs) { return (lhs == rhs.m_e); } + typedef std::vector V3StringList; typedef std::set V3StringSet; +//###################################################################### +// V3Options - Command line options + class V3Options { + public: + + private: // TYPES typedef std::map DebugSrcMap; @@ -126,6 +159,7 @@ class V3Options { int m_threads; // main switch: --threads (0 == --no-threads) int m_threadsMaxMTasks; // main switch: --threads-max-mtasks int m_traceDepth; // main switch: --trace-depth + TraceFormat m_traceFormat; // main switch: --trace or --trace-lxt2 int m_traceMaxArray;// main switch: --trace-max-array int m_traceMaxWidth;// main switch: --trace-max-width int m_unrollCount; // main switch: --unroll-count @@ -284,7 +318,8 @@ class V3Options { int threads() const { return m_threads; } int threadsMaxMTasks() const { return m_threadsMaxMTasks; } bool mtasks() const { return (m_threads > 1); } - int traceDepth() const { return m_traceDepth; } + int traceDepth() const { return m_traceDepth; } + TraceFormat traceFormat() const { return m_traceFormat; } int traceMaxArray() const { return m_traceMaxArray; } int traceMaxWidth() const { return m_traceMaxWidth; } int unrollCount() const { return m_unrollCount; } @@ -344,8 +379,8 @@ class V3Options { bool oSubstConst() const { return m_oSubstConst; } bool oTable() const { return m_oTable; } - // METHODS (uses above) - string traceClassBase() const { return "VerilatedVcd"; } + string traceClassBase() const { return m_traceFormat.classBase(); } + string traceSourceName() const { return m_traceFormat.sourceName(); } // METHODS (from main) static string version(); diff --git a/test_regress/driver.pl b/test_regress/driver.pl index a98fe5a9f..bbc8a9400 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -431,7 +431,6 @@ sub new { $self->{status_filename} ||= "$self->{obj_dir}/V".$self->{name}.".status"; $self->{run_log_filename} ||= "$self->{obj_dir}/vlt_sim.log"; $self->{coverage_filename} ||= "$self->{obj_dir}/coverage.dat"; - $self->{vcd_filename} ||= "$self->{obj_dir}/sim.vcd"; $self->{main_filename} ||= "$self->{obj_dir}/$self->{VM_PREFIX}__main.cpp"; ($self->{top_filename} = $self->{pl_filename}) =~ s/\.pl$//; if (-e ($self->{top_filename}.".vhd")) { # If VHDL file exists @@ -588,7 +587,10 @@ sub compile_vlt_flags { @{$param{verilator_flags2}}, @{$param{verilator_flags3}}); $self->{sc} = 1 if ($checkflags =~ /-sc\b/); - $self->{trace} = 1 if ($opt_trace || $checkflags =~ /-trace\b/); + $self->{trace} = ($opt_trace || $checkflags =~ /-trace\b/ || $checkflags =~ /-trace-lxt2\b/); + $self->{trace_format} = (($checkflags =~ /-trace-lxt2\b/ && 'lxt2-c') + || ($self->{sc} && 'vcd-sc') + || (!$self->{sc} && 'vcd-c')); $self->{savable} = 1 if ($checkflags =~ /-savable\b/); $self->{coverage} = 1 if ($checkflags =~ /-coverage\b/); @@ -1012,6 +1014,12 @@ sub have_sc { return 0; } +sub trace_filename { + my $self = shift; + return "$self->{obj_dir}/simx.lxt2" if $self->{trace_format} =~ /^lxt2/; + return "$self->{obj_dir}/simx.vcd"; +} + #---------------------------------------------------------------------- sub run { @@ -1177,8 +1185,9 @@ sub _make_main { print $fh "// General headers\n"; print $fh "#include \"verilated.h\"\n"; print $fh "#include \"systemc.h\"\n" if $self->sc; - print $fh "#include \"verilated_vcd_c.h\"\n" if $self->{trace} && !$self->sc; - print $fh "#include \"verilated_vcd_sc.h\"\n" if $self->{trace} && $self->sc; + print $fh "#include \"verilated_lxt2_c.h\"\n" if $self->{trace} && $self->{trace_format} eq 'lxt2-c'; + print $fh "#include \"verilated_vcd_c.h\"\n" if $self->{trace} && $self->{trace_format} eq 'vcd-c'; + print $fh "#include \"verilated_vcd_sc.h\"\n" if $self->{trace} && $self->{trace_format} eq 'vcd-sc'; print $fh "#include \"verilated_save.h\"\n" if $self->{savable}; print $fh "$VM_PREFIX * topp;\n"; @@ -1240,10 +1249,11 @@ sub _make_main { $fh->print("\n"); $fh->print("#if VM_TRACE\n"); $fh->print(" Verilated::traceEverOn(true);\n"); - $fh->print(" VerilatedVcdC* tfp = new VerilatedVcdC;\n") if !$self->sc; - $fh->print(" VerilatedVcdSc* tfp = new VerilatedVcdSc;\n") if $self->sc; + $fh->print(" VerilatedVcdC* tfp = new VerilatedVcdC;\n") if $self->{trace_format} eq 'vcd-c'; + $fh->print(" VerilatedVcdSc* tfp = new VerilatedVcdSc;\n") if $self->{trace_format} eq 'vcd-sc'; + $fh->print(" VerilatedLxt2C* tfp = new VerilatedLxt2C;\n") if $self->{trace_format} eq 'lxt2-c'; $fh->print(" topp->trace(tfp, 99);\n"); - $fh->print(" tfp->open(\"$self->{obj_dir}/simx.vcd\");\n"); + $fh->print(" tfp->open(\"".$self->trace_filename."\");\n"); if ($self->{trace} && !$self->sc) { $fh->print(" if (tfp) tfp->dump (main_time);\n"); } @@ -1376,8 +1386,8 @@ sub _make_top_v { print $fh "\n"; print $fh "`ifdef WAVES\n"; print $fh " initial begin\n"; - print $fh " \$display(\"-Tracing Waves to Dumpfile: $self->{vcd_filename}\");\n"; - print $fh " \$dumpfile(\"$self->{vcd_filename}\");\n"; + print $fh " \$display(\"-Tracing Waves to Dumpfile: ".$self->trace_filename."\");\n"; + print $fh " \$dumpfile(\"".$self->trace_filename."\");\n"; print $fh " \$dumpvars(0, top);\n"; print $fh " end\n"; print $fh "`endif\n"; @@ -1594,6 +1604,23 @@ sub vcd_identical { return 1; } +sub lxt2vcd { + my $self = (ref $_[0]? shift : $Self); + my $fn1 = shift; + my $fn2 = shift; + if (!-r $fn1) { $self->error("File does not exist $fn1\n"); return 0; } + my $cmd = qq{lxt2vcd --help}; + print "\t$cmd\n" if $::Debug; + my $out = `$cmd`; + if ($out !~ /Usage:/) { $self->skip("No lxt2vcd installed\n"); return 0; } + + $cmd = qq{lxt2vcd "$fn1" -o "$fn2"}; + print "\t$cmd\n" if $::Debug; + $out = `$cmd`; + return 1; +} + + sub _vcd_read { my $self = (ref $_[0]? shift : $Self); my $filename = shift; diff --git a/test_regress/t/t_dist_cinclude.pl b/test_regress/t/t_dist_cinclude.pl index b6585d475..7b8f0ffd2 100755 --- a/test_regress/t/t_dist_cinclude.pl +++ b/test_regress/t/t_dist_cinclude.pl @@ -24,7 +24,8 @@ if (!-r "$root/.git") { my $cmd = "cd $root && fgrep -n include $files | sort"; my $grep = `$cmd`; foreach my $line (split /\n/, $grep) { - next if $line =~ /vpi_user.h/; # IEEE Standard file - can't change it + next if $line =~ m!include/vltstd/vpi_user.h!; # IEEE Standard file - can't change it + next if $line =~ m!include/lxt2/!; # Standard file - can't change it my $hit; $hit = 1 if $line =~ /\bassert\.h/; $hit = 1 if $line =~ /\bctype\.h/; diff --git a/test_regress/t/t_trace_array_lxt2.out b/test_regress/t/t_trace_array_lxt2.out new file mode 100644 index 000000000..6d424204f --- /dev/null +++ b/test_regress/t/t_trace_array_lxt2.out @@ -0,0 +1,57 @@ +$date + Tue Aug 28 17:08:13 2018 +$end +$version + lxt2vcd +$end +$timescale 1ns $end +$scope module top $end +$var wire 1 ! clk $end +$scope module t $end +$scope module biggie $end +$var wire 1048577 " d [1048576:0] $end +$upscope $end +$var wire 32 # cyc [31:0] $end +$var wire 1 ! clk $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +#0 +0! +b0 " +b0 # +#10 +b1 # +b111111101110110111111010110011100 " +1! +#15 +0! +#20 +1! +b1111111011101101111110101100111000 " +b10 # +#25 +0! +#30 +1! +b11 # +b11111110111011011111101011001110000 " +#35 +0! +#40 +1! +b111111101110110111111010110011100000 " +b100 # +#45 +0! +#50 +1! +b101 # +b1111111011101101111110101100111000000 " +#55 +0! +#60 +1! +b11111110111011011111101011001110000000 " +b110 # diff --git a/test_regress/t/t_trace_array_lxt2.pl b/test_regress/t/t_trace_array_lxt2.pl new file mode 100755 index 000000000..5b712d27c --- /dev/null +++ b/test_regress/t/t_trace_array_lxt2.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +scenarios(vlt => 1); + +top_filename("t/t_trace_array.v"); + +compile( + verilator_flags2 => ['--cc --trace-lxt2 --trace-structs'], + ); + +execute( + check_finished => 1, + ); + +lxt2vcd($Self->trace_filename, "$Self->{obj_dir}/simx-lxt2vcd.vcd"); +vcd_identical("$Self->{obj_dir}/simx-lxt2vcd.vcd", "t/$Self->{name}.out"); + +ok(1); +1; diff --git a/test_regress/t/t_trace_complex_lxt2.out b/test_regress/t/t_trace_complex_lxt2.out new file mode 100644 index 000000000..8b6ba1fc8 --- /dev/null +++ b/test_regress/t/t_trace_complex_lxt2.out @@ -0,0 +1,193 @@ +$date + Tue Aug 28 15:03:55 2018 +$end +$version + lxt2vcd +$end +$timescale 1ns $end +$scope module top $end +$scope module $unit $end +$var wire 1 ! global_bit $end +$upscope $end +$var wire 1 " clk $end +$scope module t $end +$var wire 32 # cyc [31:0] $end +$scope module p2 $end +$var wire 32 $ PARAM [31:0] $end +$upscope $end +$scope module p3 $end +$var wire 32 % PARAM [31:0] $end +$upscope $end +$scope module unnamedblk1 $end +$var wire 32 & b [31:0] $end +$scope module unnamedblk2 $end +$var wire 32 ' a [31:0] $end +$upscope $end +$upscope $end +$var real 1 ( v_arr_real(0) $end +$var real 1 ) v_arr_real(1) $end +$var wire 2 * v_arrp [2:1] $end +$var wire 4 + v_arrp_arrp [3:0] $end +$var wire 4 , v_arrp_strp [3:0] $end +$var wire 1 - v_arru(1) $end +$var wire 1 . v_arru(2) $end +$var wire 2 / v_arru_arrp(3) [2:1] $end +$var wire 2 0 v_arru_arrp(4) [2:1] $end +$var wire 1 1 v_arru_arru(3)(1) $end +$var wire 1 2 v_arru_arru(3)(2) $end +$var wire 1 3 v_arru_arru(4)(1) $end +$var wire 1 4 v_arru_arru(4)(2) $end +$var wire 2 5 v_arru_strp(3) [1:0] $end +$var wire 2 6 v_arru_strp(4) [1:0] $end +$var real 1 7 v_real $end +$var wire 64 8 v_str32x2 [63:0] $end +$var wire 2 9 v_strp [1:0] $end +$var wire 4 : v_strp_strp [3:0] $end +$var wire 2 ; v_unip_strp [1:0] $end +$var wire 1 " clk $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +#0 +1! +0" +b0 # +b10 $ +b11 % +b0 & +b0 ' +r0 ( +r0 ) +b0 * +b0 + +b0 , +0- +0. +b0 / +b0 0 +01 +02 +03 +04 +b0 5 +b0 6 +r0 7 +b11111111 8 +b0 9 +b0 : +b0 ; +#10 +b11 ; +b1111 : +b11 9 +b100000000000000000000000011111110 8 +r0.1 7 +b11 6 +b11 5 +b11 0 +b11 / +b1111 , +b1111 + +b11 * +r0.3 ) +r0.2 ( +b101 ' +b101 & +b1 # +1" +#15 +0" +#20 +1" +b10 # +r0.4 ( +r0.6 ) +b0 * +b0 + +b0 , +b0 / +b0 0 +b0 5 +b0 6 +r0.2 7 +b1000000000000000000000000011111101 8 +b0 9 +b0 : +b0 ; +#25 +0" +#30 +1" +b11 ; +b1111 : +b11 9 +b1100000000000000000000000011111100 8 +r0.3 7 +b11 6 +b11 5 +b11 0 +b11 / +b1111 , +b1111 + +b11 * +r0.8999999999999999 ) +r0.6000000000000001 ( +b11 # +#35 +0" +#40 +1" +b100 # +r0.8 ( +r1.2 ) +b0 * +b0 + +b0 , +b0 / +b0 0 +b0 5 +b0 6 +r0.4 7 +b10000000000000000000000000011111011 8 +b0 9 +b0 : +b0 ; +#45 +0" +#50 +1" +b11 ; +b1111 : +b11 9 +b10100000000000000000000000011111010 8 +r0.5 7 +b11 6 +b11 5 +b11 0 +b11 / +b1111 , +b1111 + +b11 * +r1.5 ) +r1 ( +b101 # +#55 +0" +#60 +1" +b110 # +r1.2 ( +r1.8 ) +b0 * +b0 + +b0 , +b0 / +b0 0 +b0 5 +b0 6 +r0.6 7 +b11000000000000000000000000011111001 8 +b0 9 +b0 : +b0 ; diff --git a/test_regress/t/t_trace_complex_lxt2.pl b/test_regress/t/t_trace_complex_lxt2.pl new file mode 100755 index 000000000..57dc059e6 --- /dev/null +++ b/test_regress/t/t_trace_complex_lxt2.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +scenarios(simulator => 1); + +top_filename("t/t_trace_complex.v"); + +compile( + verilator_flags2 => ['--cc --trace-lxt2'], + ); + +execute( + check_finished => 1, + ); + +lxt2vcd($Self->trace_filename, "$Self->{obj_dir}/simx-lxt2vcd.vcd"); +vcd_identical("$Self->{obj_dir}/simx-lxt2vcd.vcd", "t/$Self->{name}.out"); + +ok(1); +1; diff --git a/test_regress/t/t_trace_complex_params_lxt2.out b/test_regress/t/t_trace_complex_params_lxt2.out new file mode 100644 index 000000000..552bc58fe --- /dev/null +++ b/test_regress/t/t_trace_complex_params_lxt2.out @@ -0,0 +1,193 @@ +$date + Tue Aug 28 15:09:58 2018 +$end +$version + lxt2vcd +$end +$timescale 1ns $end +$scope module top $end +$scope module $unit $end +$var wire 1 ! global_bit $end +$upscope $end +$var wire 1 " clk $end +$scope module t $end +$var wire 32 # cyc [31:0] $end +$scope module p2 $end +$var wire 32 $ PARAM [31:0] $end +$upscope $end +$scope module p3 $end +$var wire 32 % PARAM [31:0] $end +$upscope $end +$scope module unnamedblk1 $end +$var wire 32 & b [31:0] $end +$scope module unnamedblk2 $end +$var wire 32 ' a [31:0] $end +$upscope $end +$upscope $end +$var real 1 ( v_arr_real(0) $end +$var real 1 ) v_arr_real(1) $end +$var wire 2 * v_arrp [2:1] $end +$var wire 4 + v_arrp_arrp [3:0] $end +$var wire 4 , v_arrp_strp [3:0] $end +$var wire 1 - v_arru(1) $end +$var wire 1 . v_arru(2) $end +$var wire 2 / v_arru_arrp(3) [2:1] $end +$var wire 2 0 v_arru_arrp(4) [2:1] $end +$var wire 1 1 v_arru_arru(3)(1) $end +$var wire 1 2 v_arru_arru(3)(2) $end +$var wire 1 3 v_arru_arru(4)(1) $end +$var wire 1 4 v_arru_arru(4)(2) $end +$var wire 2 5 v_arru_strp(3) [1:0] $end +$var wire 2 6 v_arru_strp(4) [1:0] $end +$var real 1 7 v_real $end +$var wire 64 8 v_str32x2 [63:0] $end +$var wire 2 9 v_strp [1:0] $end +$var wire 4 : v_strp_strp [3:0] $end +$var wire 2 ; v_unip_strp [1:0] $end +$var wire 1 " clk $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +#0 +1! +0" +b0 # +b10 $ +b11 % +b0 & +b0 ' +r0 ( +r0 ) +b0 * +b0 + +b0 , +0- +0. +b0 / +b0 0 +01 +02 +03 +04 +b0 5 +b0 6 +r0 7 +b11111111 8 +b0 9 +b0 : +b0 ; +#10 +b11 ; +b1111 : +b11 9 +b100000000000000000000000011111110 8 +r0.1 7 +b11 6 +b11 5 +b11 0 +b11 / +b1111 , +b1111 + +b11 * +r0.3 ) +r0.2 ( +b101 ' +b101 & +b1 # +1" +#15 +0" +#20 +1" +b10 # +r0.4 ( +r0.6 ) +b0 * +b0 + +b0 , +b0 / +b0 0 +b0 5 +b0 6 +r0.2 7 +b1000000000000000000000000011111101 8 +b0 9 +b0 : +b0 ; +#25 +0" +#30 +1" +b11 ; +b1111 : +b11 9 +b1100000000000000000000000011111100 8 +r0.3 7 +b11 6 +b11 5 +b11 0 +b11 / +b1111 , +b1111 + +b11 * +r0.8999999999999999 ) +r0.6000000000000001 ( +b11 # +#35 +0" +#40 +1" +b100 # +r0.8 ( +r1.2 ) +b0 * +b0 + +b0 , +b0 / +b0 0 +b0 5 +b0 6 +r0.4 7 +b10000000000000000000000000011111011 8 +b0 9 +b0 : +b0 ; +#45 +0" +#50 +1" +b11 ; +b1111 : +b11 9 +b10100000000000000000000000011111010 8 +r0.5 7 +b11 6 +b11 5 +b11 0 +b11 / +b1111 , +b1111 + +b11 * +r1.5 ) +r1 ( +b101 # +#55 +0" +#60 +1" +b110 # +r1.2 ( +r1.8 ) +b0 * +b0 + +b0 , +b0 / +b0 0 +b0 5 +b0 6 +r0.6 7 +b11000000000000000000000000011111001 8 +b0 9 +b0 : +b0 ; diff --git a/test_regress/t/t_trace_complex_params_lxt2.pl b/test_regress/t/t_trace_complex_params_lxt2.pl new file mode 100755 index 000000000..7bfeb390e --- /dev/null +++ b/test_regress/t/t_trace_complex_params_lxt2.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +scenarios(simulator => 1); + +top_filename("t_trace_complex.v"); + +compile( + verilator_flags2 => ['--cc --trace-lxt2 --no-trace-structs --trace-params'], + ); + +execute( + check_finished => 1, + ); + +lxt2vcd($Self->trace_filename, "$Self->{obj_dir}/simx-lxt2vcd.vcd"); +vcd_identical("$Self->{obj_dir}/simx-lxt2vcd.vcd", "t/$Self->{name}.out"); + +ok(1); +1; diff --git a/test_regress/t/t_trace_complex_structs_lxt2.out b/test_regress/t/t_trace_complex_structs_lxt2.out new file mode 100644 index 000000000..52966de23 --- /dev/null +++ b/test_regress/t/t_trace_complex_structs_lxt2.out @@ -0,0 +1,311 @@ +$date + Tue Aug 28 15:07:10 2018 +$end +$version + lxt2vcd +$end +$timescale 1ns $end +$scope module top $end +$scope module $unit $end +$var wire 1 ! global_bit $end +$upscope $end +$var wire 1 " clk $end +$scope module t $end +$var wire 32 # cyc [31:0] $end +$scope module unnamedblk1 $end +$var wire 32 $ b [31:0] $end +$scope module unnamedblk2 $end +$var wire 32 % a [31:0] $end +$upscope $end +$upscope $end +$var real 1 & v_arr_real(0) $end +$var real 1 ' v_arr_real(1) $end +$var wire 2 ( v_arrp [2:1] $end +$var wire 2 ) v_arrp_arrp(3) [1:0] $end +$var wire 2 * v_arrp_arrp(4) [1:0] $end +$scope module v_arrp_strp(3) $end +$var wire 1 + b0 $end +$var wire 1 , b1 $end +$upscope $end +$scope module v_arrp_strp(4) $end +$var wire 1 - b0 $end +$var wire 1 . b1 $end +$upscope $end +$var wire 1 / v_arru(1) $end +$var wire 1 0 v_arru(2) $end +$var wire 2 1 v_arru_arrp(3) [2:1] $end +$var wire 2 2 v_arru_arrp(4) [2:1] $end +$var wire 1 3 v_arru_arru(3)(1) $end +$var wire 1 4 v_arru_arru(3)(2) $end +$var wire 1 5 v_arru_arru(4)(1) $end +$var wire 1 6 v_arru_arru(4)(2) $end +$scope module v_arru_strp(3) $end +$var wire 1 7 b0 $end +$var wire 1 8 b1 $end +$upscope $end +$scope module v_arru_strp(4) $end +$var wire 1 9 b0 $end +$var wire 1 : b1 $end +$upscope $end +$var real 1 ; v_real $end +$scope module v_str32x2(0) $end +$var wire 32 < data [31:0] $end +$upscope $end +$scope module v_str32x2(1) $end +$var wire 32 = data [31:0] $end +$upscope $end +$scope module v_strp $end +$var wire 1 > b0 $end +$var wire 1 ? b1 $end +$upscope $end +$scope module v_strp_strp $end +$scope module x0 $end +$var wire 1 @ b0 $end +$var wire 1 A b1 $end +$upscope $end +$scope module x1 $end +$var wire 1 B b0 $end +$var wire 1 C b1 $end +$upscope $end +$upscope $end +$scope module v_unip_strp $end +$scope module x1 $end +$var wire 1 D b0 $end +$var wire 1 E b1 $end +$upscope $end +$upscope $end +$var wire 1 " clk $end +$scope module v_unip_strp $end +$scope module x0 $end +$var wire 1 D b0 $end +$var wire 1 E b1 $end +$upscope $end +$upscope $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +#0 +1! +0" +b0 # +b0 $ +b0 % +r0 & +r0 ' +b0 ( +b0 ) +b0 * +0+ +0, +0- +0. +0/ +00 +b0 1 +b0 2 +03 +04 +05 +06 +07 +08 +09 +0: +r0 ; +b11111111 < +b0 = +0> +0? +0@ +0A +0B +0C +0D +0E +#10 +1E +1D +1C +1B +1A +1@ +1? +1> +b1 = +b11111110 < +r0.1 ; +1: +19 +18 +17 +b11 2 +b11 1 +1. +1- +1, +1+ +b11 * +b11 ) +b11 ( +r0.3 ' +r0.2 & +b101 % +b101 $ +b1 # +1" +#15 +0" +#20 +1" +b10 # +r0.4 & +r0.6 ' +b0 ( +b0 ) +b0 * +0+ +0, +0- +0. +b0 1 +b0 2 +07 +08 +09 +0: +r0.2 ; +b11111101 < +b10 = +0> +0? +0@ +0A +0B +0C +0D +0E +#25 +0" +#30 +1" +1E +1D +1C +1B +1A +1@ +1? +1> +b11 = +b11111100 < +r0.3 ; +1: +19 +18 +17 +b11 2 +b11 1 +1. +1- +1, +1+ +b11 * +b11 ) +b11 ( +r0.8999999999999999 ' +r0.6000000000000001 & +b11 # +#35 +0" +#40 +1" +b100 # +r0.8 & +r1.2 ' +b0 ( +b0 ) +b0 * +0+ +0, +0- +0. +b0 1 +b0 2 +07 +08 +09 +0: +r0.4 ; +b11111011 < +b100 = +0> +0? +0@ +0A +0B +0C +0D +0E +#45 +0" +#50 +1" +1E +1D +1C +1B +1A +1@ +1? +1> +b101 = +b11111010 < +r0.5 ; +1: +19 +18 +17 +b11 2 +b11 1 +1. +1- +1, +1+ +b11 * +b11 ) +b11 ( +r1.5 ' +r1 & +b101 # +#55 +0" +#60 +1" +b110 # +r1.2 & +r1.8 ' +b0 ( +b0 ) +b0 * +0+ +0, +0- +0. +b0 1 +b0 2 +07 +08 +09 +0: +r0.6 ; +b11111001 < +b110 = +0> +0? +0@ +0A +0B +0C +0D +0E diff --git a/test_regress/t/t_trace_complex_structs_lxt2.pl b/test_regress/t/t_trace_complex_structs_lxt2.pl new file mode 100755 index 000000000..bd38cbb57 --- /dev/null +++ b/test_regress/t/t_trace_complex_structs_lxt2.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +scenarios(simulator => 1); + +top_filename("t_trace_complex.v"); + +compile( + verilator_flags2 => ['--cc --trace-lxt2 --trace-structs --no-trace-params'], + ); + +execute( + check_finished => 1, + ); + +lxt2vcd($Self->trace_filename, "$Self->{obj_dir}/simx-lxt2vcd.vcd"); +vcd_identical("$Self->{obj_dir}/simx-lxt2vcd.vcd", "t/$Self->{name}.out"); + +ok(1); +1; diff --git a/test_regress/t/t_trace_lxt2.out b/test_regress/t/t_trace_lxt2.out new file mode 100644 index 000000000..10da98fcf --- /dev/null +++ b/test_regress/t/t_trace_lxt2.out @@ -0,0 +1,993 @@ +$date + Tue Aug 28 05:58:13 2018 +$end +$version + lxt2vcd +$end +$timescale 1ns $end +$scope module top $end +$var wire 1 ! clk $end +$var wire 5 " state [4:0] $end +$scope module t $end +$var wire 32 # cyc [31:0] $end +$var wire 1 $ rstn $end +$scope module test $end +$var wire 5 % state_array(0) [4:0] $end +$var wire 5 & state_array(1) [4:0] $end +$var wire 5 ' state_array(2) [4:0] $end +$var wire 5 ( state_w [4:0] $end +$scope module unnamedblk1 $end +$var wire 32 ) i [31:0] $end +$upscope $end +$scope module unnamedblk2 $end +$var wire 32 * i [31:0] $end +$upscope $end +$upscope $end +$var wire 1 ! clk $end +$var wire 5 " state [4:0] $end +$scope module test $end +$var wire 1 ! clk $end +$var wire 1 $ rstn $end +$var wire 5 " state [4:0] $end +$upscope $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +#0 +0! +b0 " +b0 # +0$ +b0 % +b0 & +b0 ' +b0 ( +b0 ) +b0 * +#10 +b11 ) +b10100 ( +b1 ' +b1 & +b1 % +b1 # +b1 " +1! +#15 +0! +#20 +1! +b10 # +#25 +0! +#30 +1! +b11 # +#35 +0! +#40 +1! +b100 # +#45 +0! +#50 +1! +b101 # +#55 +0! +#60 +1! +b110 # +#65 +0! +#70 +1! +b111 # +#75 +0! +#80 +1! +b1000 # +#85 +0! +#90 +1! +b1001 # +#95 +0! +#100 +1! +b1010 # +#105 +0! +#110 +1! +b1011 # +1$ +#115 +0! +#120 +1! +b1100 # +b10100 ' +b1010 ( +b10 * +#125 +0! +#130 +1! +b101 ( +b1010 ' +b1101 # +b10100 & +#135 +0! +#140 +1! +b1010 & +b1110 # +b101 ' +b10110 ( +b10100 " +b10100 % +#145 +0! +#150 +1! +b1010 % +b1010 " +b1011 ( +b10110 ' +b1111 # +b101 & +#155 +0! +#160 +1! +b10110 & +b10000 # +b1011 ' +b10001 ( +b101 " +b101 % +#165 +0! +#170 +1! +b10110 % +b10110 " +b11100 ( +b10001 ' +b10001 # +b1011 & +#175 +0! +#180 +1! +b10001 & +b10010 # +b11100 ' +b1110 ( +b1011 " +b1011 % +#185 +0! +#190 +1! +b10001 % +b10001 " +b111 ( +b1110 ' +b10011 # +b11100 & +#195 +0! +#200 +1! +b1110 & +b10100 # +b111 ' +b10111 ( +b11100 " +b11100 % +#205 +0! +#210 +1! +b1110 % +b1110 " +b11111 ( +b10111 ' +b10101 # +b111 & +#215 +0! +#220 +1! +b10111 & +b10110 # +b11111 ' +b11011 ( +b111 " +b111 % +#225 +0! +#230 +1! +b10111 % +b10111 " +b11001 ( +b11011 ' +b10111 # +b11111 & +#235 +0! +#240 +1! +b11011 & +b11000 # +b11001 ' +b11000 ( +b11111 " +b11111 % +#245 +0! +#250 +1! +b11011 % +b11011 " +b1100 ( +b11000 ' +b11001 # +b11001 & +#255 +0! +#260 +1! +b11000 & +b11010 # +b1100 ' +b110 ( +b11001 " +b11001 % +#265 +0! +#270 +1! +b11000 % +b11000 " +b11 ( +b110 ' +b11011 # +b1100 & +#275 +0! +#280 +1! +b110 & +b11100 # +b11 ' +b10101 ( +b1100 " +b1100 % +#285 +0! +#290 +1! +b110 % +b110 " +b11110 ( +b10101 ' +b11101 # +b11 & +#295 +0! +#300 +1! +b10101 & +b11110 # +b11110 ' +b1111 ( +b11 " +b11 % +#305 +0! +#310 +1! +b10101 % +b10101 " +b10011 ( +b1111 ' +b11111 # +b11110 & +#315 +0! +#320 +1! +b1111 & +b100000 # +b10011 ' +b11101 ( +b11110 " +b11110 % +#325 +0! +#330 +1! +b11010 ( +b11101 ' +b10011 & +b1111 % +b100001 # +b1111 " +#335 +0! +#340 +1! +b10011 " +b100010 # +b10011 % +b11101 & +b11010 ' +b1101 ( +#345 +0! +#350 +1! +b10010 ( +b1101 ' +b11010 & +b11101 % +b100011 # +b11101 " +#355 +0! +#360 +1! +b11010 " +b100100 # +b11010 % +b1101 & +b10010 ' +b1001 ( +#365 +0! +#370 +1! +b10000 ( +b1001 ' +b10010 & +b1101 % +b100101 # +b1101 " +#375 +0! +#380 +1! +b10010 " +b100110 # +b10010 % +b1001 & +b10000 ' +b1000 ( +#385 +0! +#390 +1! +b100 ( +b1000 ' +b10000 & +b1001 % +b100111 # +b1001 " +#395 +0! +#400 +1! +b10000 " +b101000 # +b10000 % +b1000 & +b100 ' +b10 ( +#405 +0! +#410 +1! +b1 ( +b10 ' +b100 & +b1000 % +b101001 # +b1000 " +#415 +0! +#420 +1! +b100 " +b101010 # +b100 % +b10 & +b1 ' +b10100 ( +#425 +0! +#430 +1! +b1010 ( +b10100 ' +b1 & +b10 % +b101011 # +b10 " +#435 +0! +#440 +1! +b1 " +b101100 # +b1 % +b10100 & +b1010 ' +b101 ( +#445 +0! +#450 +1! +b10110 ( +b101 ' +b1010 & +b10100 % +b101101 # +b10100 " +#455 +0! +#460 +1! +b1010 " +b101110 # +b1010 % +b101 & +b10110 ' +b1011 ( +#465 +0! +#470 +1! +b10001 ( +b1011 ' +b10110 & +b101 % +b101111 # +b101 " +#475 +0! +#480 +1! +b10110 " +b110000 # +b10110 % +b1011 & +b10001 ' +b11100 ( +#485 +0! +#490 +1! +b1110 ( +b11100 ' +b10001 & +b1011 % +b110001 # +b1011 " +#495 +0! +#500 +1! +b10001 " +b110010 # +b10001 % +b11100 & +b1110 ' +b111 ( +#505 +0! +#510 +1! +b10111 ( +b111 ' +b1110 & +b11100 % +b110011 # +b11100 " +#515 +0! +#520 +1! +b1110 " +b110100 # +b1110 % +b111 & +b10111 ' +b11111 ( +#525 +0! +#530 +1! +b11011 ( +b11111 ' +b10111 & +b111 % +b110101 # +b111 " +#535 +0! +#540 +1! +b10111 " +b110110 # +b10111 % +b11111 & +b11011 ' +b11001 ( +#545 +0! +#550 +1! +b11000 ( +b11001 ' +b11011 & +b11111 % +b110111 # +b11111 " +#555 +0! +#560 +1! +b11011 " +b111000 # +b11011 % +b11001 & +b11000 ' +b1100 ( +#565 +0! +#570 +1! +b110 ( +b1100 ' +b11000 & +b11001 % +b111001 # +b11001 " +#575 +0! +#580 +1! +b11000 " +b111010 # +b11000 % +b1100 & +b110 ' +b11 ( +#585 +0! +#590 +1! +b10101 ( +b11 ' +b110 & +b1100 % +b111011 # +b1100 " +#595 +0! +#600 +1! +b110 " +b111100 # +b110 % +b11 & +b10101 ' +b11110 ( +#605 +0! +#610 +1! +b1111 ( +b11110 ' +b10101 & +b11 % +b111101 # +b11 " +#615 +0! +#620 +1! +b10101 " +b111110 # +b10101 % +b11110 & +b1111 ' +b10011 ( +#625 +0! +#630 +1! +b11101 ( +b10011 ' +b1111 & +b11110 % +b111111 # +b11110 " +#635 +0! +#640 +1! +b1111 " +b1000000 # +b1111 % +b10011 & +b11101 ' +b11010 ( +#645 +0! +#650 +1! +b1101 ( +b11010 ' +b11101 & +b10011 % +b1000001 # +b10011 " +#655 +0! +#660 +1! +b11101 " +b1000010 # +b11101 % +b11010 & +b1101 ' +b10010 ( +#665 +0! +#670 +1! +b1001 ( +b10010 ' +b1101 & +b11010 % +b1000011 # +b11010 " +#675 +0! +#680 +1! +b1101 " +b1000100 # +b1101 % +b10010 & +b1001 ' +b10000 ( +#685 +0! +#690 +1! +b1000 ( +b10000 ' +b1001 & +b10010 % +b1000101 # +b10010 " +#695 +0! +#700 +1! +b1001 " +b1000110 # +b1001 % +b10000 & +b1000 ' +b100 ( +#705 +0! +#710 +1! +b10 ( +b100 ' +b1000 & +b10000 % +b1000111 # +b10000 " +#715 +0! +#720 +1! +b1000 " +b1001000 # +b1000 % +b100 & +b10 ' +b1 ( +#725 +0! +#730 +1! +b10100 ( +b1 ' +b10 & +b100 % +b1001001 # +b100 " +#735 +0! +#740 +1! +b10 " +b1001010 # +b10 % +b1 & +b10100 ' +b1010 ( +#745 +0! +#750 +1! +b101 ( +b1010 ' +b10100 & +b1 % +b1001011 # +b1 " +#755 +0! +#760 +1! +b10100 " +b1001100 # +b10100 % +b1010 & +b101 ' +b10110 ( +#765 +0! +#770 +1! +b1011 ( +b10110 ' +b101 & +b1010 % +b1001101 # +b1010 " +#775 +0! +#780 +1! +b101 " +b1001110 # +b101 % +b10110 & +b1011 ' +b10001 ( +#785 +0! +#790 +1! +b11100 ( +b10001 ' +b1011 & +b10110 % +b1001111 # +b10110 " +#795 +0! +#800 +1! +b1011 " +b1010000 # +b1011 % +b10001 & +b11100 ' +b1110 ( +#805 +0! +#810 +1! +b111 ( +b1110 ' +b11100 & +b10001 % +b1010001 # +b10001 " +#815 +0! +#820 +1! +b11100 " +b1010010 # +b11100 % +b1110 & +b111 ' +b10111 ( +#825 +0! +#830 +1! +b11111 ( +b10111 ' +b111 & +b1110 % +b1010011 # +b1110 " +#835 +0! +#840 +1! +b111 " +b1010100 # +b111 % +b10111 & +b11111 ' +b11011 ( +#845 +0! +#850 +1! +b11001 ( +b11011 ' +b11111 & +b10111 % +b1010101 # +b10111 " +#855 +0! +#860 +1! +b11111 " +b1010110 # +b11111 % +b11011 & +b11001 ' +b11000 ( +#865 +0! +#870 +1! +b1100 ( +b11000 ' +b11001 & +b11011 % +b1010111 # +b11011 " +#875 +0! +#880 +1! +b11001 " +b1011000 # +b11001 % +b11000 & +b1100 ' +b110 ( +#885 +0! +#890 +1! +b11 ( +b110 ' +b1100 & +b11000 % +b1011001 # +b11000 " +#895 +0! +#900 +1! +b1100 " +b1011010 # +b1100 % +b110 & +b11 ' +b10101 ( +#905 +0! +#910 +1! +b11110 ( +b10101 ' +b11 & +b110 % +b1011011 # +b110 " +#915 +0! +#920 +1! +b11 " +b1011100 # +b11 % +b10101 & +b11110 ' +b1111 ( +#925 +0! +#930 +1! +b10011 ( +b1111 ' +b11110 & +b10101 % +b1011101 # +b10101 " +#935 +0! +#940 +1! +b11110 " +b1011110 # +b11110 % +b1111 & +b10011 ' +b11101 ( +#945 +0! +#950 +1! +b11010 ( +b11101 ' +b10011 & +b1111 % +b1011111 # +b1111 " +#955 +0! +#960 +1! +b10011 " +b1100000 # +b10011 % +b11101 & +b11010 ' +b1101 ( +#965 +0! +#970 +1! +b10010 ( +b1101 ' +b11010 & +b11101 % +b1100001 # +b11101 " +#975 +0! +#980 +1! +b11010 " +b1100010 # +b11010 % +b1101 & +b10010 ' +b1001 ( +#985 +0! +#990 +1! +b10000 ( +b1001 ' +b10010 & +b1101 % +b1100011 # +b1101 " +#995 +0! +#1000 +1! +b10010 " +b1100100 # +b10010 % +b1001 & +b10000 ' +b1000 ( diff --git a/test_regress/t/t_trace_lxt2.pl b/test_regress/t/t_trace_lxt2.pl new file mode 100755 index 000000000..564b5be89 --- /dev/null +++ b/test_regress/t/t_trace_lxt2.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl +# This file ONLY is placed into the Public Domain, for any use, +# Author: Yu-Sheng Lin johnjohnlys@media.ee.ntu.edu.tw + +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } + +scenarios(vlt_all => 1); + +compile( + v_flags2 => ["--trace-lxt2"], +); + +execute( + check_finished => 1, +); + +lxt2vcd($Self->trace_filename, "$Self->{obj_dir}/simx-lxt2vcd.vcd"); +vcd_identical("$Self->{obj_dir}/simx-lxt2vcd.vcd", "t/$Self->{name}.out"); + +ok(1); +1; + diff --git a/test_regress/t/t_trace_lxt2.v b/test_regress/t/t_trace_lxt2.v new file mode 100644 index 000000000..779058aa3 --- /dev/null +++ b/test_regress/t/t_trace_lxt2.v @@ -0,0 +1,76 @@ +// This file ONLY is placed into the Public Domain, for any use, +// Author: Yu-Sheng Lin johnjohnlys@media.ee.ntu.edu.tw + +module t (/*AUTOARG*/ + // Outputs + state, + // Inputs + clk + ); + + input clk; + + int cyc; + reg rstn; + output [4:0] state; + + Test test (/*AUTOINST*/ + // Outputs + .state (state[4:0]), + // Inputs + .clk (clk), + .rstn (rstn)); + + // Test loop + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==0) begin + // Setup + rstn <= ~'1; + end + else if (cyc<10) begin + rstn <= ~'1; + end + else if (cyc<90) begin + rstn <= ~'0; + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + + +module Test ( + input clk, + input rstn, + output logic [4:0] state + ); + + logic [4:0] state_w; + logic [4:0] state_array [3]; + assign state = state_array[0]; + + always_comb begin + state_w[4] = state_array[2][0]; + state_w[3] = state_array[2][4]; + state_w[2] = state_array[2][3] ^ state_array[2][0]; + state_w[1] = state_array[2][2]; + state_w[0] = state_array[2][1]; + end + + always_ff @(posedge clk or negedge rstn) begin + if (!rstn) begin + for (int i = 0; i < 3; i++) + state_array[i] <= 'b1; + end + else begin + for (int i = 0; i < 2; i++) + state_array[i] <= state_array[i+1]; + state_array[2] <= state_w; + end + end + +endmodule diff --git a/test_regress/t/t_trace_packed_struct.v b/test_regress/t/t_trace_packed_struct.v index 4caac627c..1c800d0d5 100644 --- a/test_regress/t/t_trace_packed_struct.v +++ b/test_regress/t/t_trace_packed_struct.v @@ -9,6 +9,7 @@ module t (/*AUTOARG*/ ); input clk; + int cnt; // This won't compile with tracing as an incorrect declaration is made for // the temp variables used to represent the elements of localparam v @@ -22,8 +23,14 @@ module t (/*AUTOARG*/ '{'{32'h30000002, 32'h30000001, 32'h30000000}} }; - initial begin - $write("*-* All Finished *-*\n"); - $finish; + initial cnt = 0; + always@(posedge clk) begin + if (cnt < 3) begin + cnt = cnt + 1; + end + else begin + $write("*-* All Finished *-*\n"); + $finish; + end end endmodule diff --git a/test_regress/t/t_trace_packed_struct_lxt2.out b/test_regress/t/t_trace_packed_struct_lxt2.out new file mode 100644 index 000000000..253fa7f57 --- /dev/null +++ b/test_regress/t/t_trace_packed_struct_lxt2.out @@ -0,0 +1,42 @@ +$date + Tue Aug 28 16:59:34 2018 +$end +$version + lxt2vcd +$end +$timescale 1ns $end +$scope module top $end +$var wire 1 ! clk $end +$scope module t $end +$var wire 32 " cnt [31:0] $end +$var wire 96 # v(0) [95:0] $end +$var wire 96 $ v(1) [95:0] $end +$var wire 96 % v(2) [95:0] $end +$var wire 1 ! clk $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +#0 +0! +b0 " +b1100000000000000000000000000100011000000000000000000000000000100110000000000000000000000000000 # +b1000000000000000000000000000100010000000000000000000000000000100100000000000000000000000000000 $ +b100000000000000000000000000100001000000000000000000000000000100010000000000000000000000000000 % +#10 +b1 " +1! +#15 +0! +#20 +1! +b10 " +#25 +0! +#30 +1! +b11 " +#35 +0! +#40 +1! diff --git a/test_regress/t/t_trace_packed_struct_lxt2.pl b/test_regress/t/t_trace_packed_struct_lxt2.pl new file mode 100755 index 000000000..9961df864 --- /dev/null +++ b/test_regress/t/t_trace_packed_struct_lxt2.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +scenarios(simulator => 1); + +top_filename("t/t_trace_packed_struct.v"); + +compile( + v_flags2 => ["--trace-lxt2"] + ); + +execute( + check_finished => 1, + ); + +lxt2vcd($Self->trace_filename, "$Self->{obj_dir}/simx-lxt2vcd.vcd"); +vcd_identical("$Self->{obj_dir}/simx-lxt2vcd.vcd", "t/$Self->{name}.out"); + +ok(1); +1; diff --git a/test_regress/t/t_trace_param_lxt2.pl b/test_regress/t/t_trace_param_lxt2.pl new file mode 100755 index 000000000..ddca7375e --- /dev/null +++ b/test_regress/t/t_trace_param_lxt2.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2013 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +scenarios(vlt_all => 1); + +top_filename("t/t_trace_param.v"); + +compile( + v_flags2 => ["--trace-lxt2"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_trace_primitive_lxt2.pl b/test_regress/t/t_trace_primitive_lxt2.pl new file mode 100755 index 000000000..53c6a93c2 --- /dev/null +++ b/test_regress/t/t_trace_primitive_lxt2.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2013 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +scenarios(simulator => 1); + +top_filename("t/t_trace_primitive.v"); + +compile( + v_flags2 => ["--trace-lxt2"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_verilated_all.pl b/test_regress/t/t_verilated_all.pl index ebd9e713a..f9094aba1 100755 --- a/test_regress/t/t_verilated_all.pl +++ b/test_regress/t/t_verilated_all.pl @@ -49,6 +49,7 @@ foreach my $dfile (glob("$Self->{obj_dir}/*.d")) { foreach my $file (sort keys %hit) { if (!$hit{$file} && $file !~ /_sc/ + && $file !~ /_lxt2/ && ($file !~ /_thread/ || $Self->cfg_with_threaded)) { error("Include file not covered by t_verilated_all test: ",$file); }