/* * Copyright (c) Tony Bybell 1999-2000. * * 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. */ /* * vcd.c 23jan99ajb * evcd parts 29jun99ajb * profiler optimizations 15jul99ajb * more profiler optimizations 25jan00ajb * finsim parameter fix 26jan00ajb * vector rechaining code 03apr00ajb * multiple var section code 06apr00ajb * stripped from gtkwave 09apr00ajb */ #include "vcd.h" #undef VCD_BSEARCH_IS_PERFECT /* bsearch is imperfect under linux, but OK under AIX */ /******************************************************************/ static void add_histent(struct globals *obj, TimeType time, struct Node *n, char ch, int regadd, char *vector); static void add_tail_histents(struct globals *obj); static void vcd_build_symbols(struct globals *obj); static void vcd_cleanup(struct globals *obj); static void evcd_strcpy(char *dst, char *src); /******************************************************************/ enum Tokens { T_VAR, T_END, T_SCOPE, T_UPSCOPE, T_COMMENT, T_DATE, T_DUMPALL, T_DUMPOFF, T_DUMPON, T_DUMPVARS, T_ENDDEFINITIONS, T_DUMPPORTS, T_DUMPPORTSOFF, T_DUMPPORTSON, T_DUMPPORTSALL, T_TIMESCALE, T_VERSION, T_VCDCLOSE, T_EOF, T_STRING, T_UNKNOWN_KEY }; char *tokens[]={ "var", "end", "scope", "upscope", "comment", "date", "dumpall", "dumpoff", "dumpon", "dumpvars", "enddefinitions", "dumpports", "dumpportsoff", "dumpportson", "dumpportsall", "timescale", "version", "vcdclose", "", "", "" }; #define NUM_TOKENS 18 #define T_GET(x) tok=get_token(x);if((tok==T_END)||(tok==T_EOF))break; /******************************************************************/ enum VarTypes { V_EVENT, V_PARAMETER, V_INTEGER, V_REAL, V_REG, V_SUPPLY0, V_SUPPLY1, V_TIME, V_TRI, V_TRIAND, V_TRIOR, V_TRIREG, V_TRI0, V_TRI1, V_WAND, V_WIRE, V_WOR, V_PORT, V_END, V_LB, V_COLON, V_RB, V_STRING }; char *vartypes[]={ "event", "parameter", "integer", "real", "reg", "supply0", "supply1", "time", "tri", "triand", "trior", "trireg", "tri0", "tri1", "wand", "wire", "wor", "port", "$end", "", "", "", ""}; #define NUM_VTOKENS 19 /******************************************************************/ /* * histent structs are NEVER freed so this is OK.. */ #define VCD_HISTENT_GRANULARITY 100 static struct HistEnt *histent_calloc(struct globals *obj) { if(obj->he_curr==obj->he_fini) { obj->he_curr=(struct HistEnt *)calloc_2(VCD_HISTENT_GRANULARITY, sizeof(struct HistEnt)); obj->he_fini=obj->he_curr+VCD_HISTENT_GRANULARITY; } return(obj->he_curr++); } /******************************************************************/ static struct queuedevent *queuedevents=NULL; /******************************************************************/ /* * bsearch compare */ static int vcdsymbsearchcompare(const void *s1, const void *s2) { char *v1; struct vcdsymbol *v2; v1=(char *)s1; v2=*((struct vcdsymbol **)s2); return(strcmp(v1, v2->id)); } /* * actual bsearch */ static struct vcdsymbol *bsearch_vcd(struct globals *obj, char *key) { struct vcdsymbol **v; struct vcdsymbol *t; v=(struct vcdsymbol **)bsearch(key, obj->sorted, obj->numsyms, sizeof(struct vcdsymbol *), vcdsymbsearchcompare); if(v) { #ifndef VCD_BSEARCH_IS_PERFECT for(;;) { t=*v; if((v==obj->sorted)||(strcmp((*(--v))->id, key))) { return(t); } } #else return(*v); #endif } else { return(NULL); } } /* * sort on vcdsymbol pointers */ static int vcdsymcompare(const void *s1, const void *s2) { struct vcdsymbol *v1, *v2; v1=*((struct vcdsymbol **)s1); v2=*((struct vcdsymbol **)s2); return(strcmp(v1->id, v2->id)); } /* * create sorted (by id) table */ static void create_sorted_table(struct globals *obj) { struct vcdsymbol *v; struct vcdsymbol **pnt; if(obj->sorted) { free_2(obj->sorted); /* this means we saw a 2nd enddefinition chunk! */ } if(obj->numsyms) { pnt=obj->sorted=(struct vcdsymbol **)calloc_2(obj->numsyms, sizeof(struct vcdsymbol *)); v=obj->vcdsymroot; while(v) { *(pnt++)=v; v=v->next; } qsort(obj->sorted, obj->numsyms, sizeof(struct vcdsymbol *), vcdsymcompare); } } /******************************************************************/ /* * single char get inlined/optimized */ static void getch_alloc(struct globals *obj) { obj->vend=obj->vst=obj->vcdbuf=(char *)calloc_2(1,VCD_BSIZ); } static void getch_free(struct globals *obj) { free_2(obj->vcdbuf); obj->vcdbuf=obj->vst=obj->vend=NULL; } static int getch_fetch(struct globals *obj) { size_t rd; if(feof(obj->vcd_handle)||errno) return(-1); obj->vcdbyteno+=(obj->vend-obj->vcdbuf); rd=fread(obj->vcdbuf, sizeof(char), VCD_BSIZ, obj->vcd_handle); obj->vend=(obj->vst=obj->vcdbuf)+rd; if(!rd) return(-1); return((int)(*(obj->vst++))); } #define getch(x) ((x->vst!=x->vend)?((int)(*(x->vst++))):(getch_fetch(x))) static int getch_patched(struct globals *obj) { char ch; ch=*obj->vsplitcurr; if(!ch) { return(-1); } else { obj->vsplitcurr++; return((int)ch); } } /* * simple tokenizer */ static int get_token(struct globals *obj) { int ch; int i, len=0; int is_string=0; for(;;) { ch=getch(obj); if(ch<0) return(T_EOF); if(ch<=' ') continue; /* val<=' ' is a quick whitespace check */ break; /* (take advantage of fact that vcd is text) */ } if(ch=='$') { obj->yytext[len++]=ch; for(;;) { ch=getch(obj); if(ch<0) return(T_EOF); if(ch<=' ') continue; break; } } else { is_string=1; } for(obj->yytext[len++]=ch;;obj->yytext[len++]=ch) { if(len==obj->T_MAX_STR) { obj->yytext=(char *)realloc_2(obj->yytext, (obj->T_MAX_STR=obj->T_MAX_STR*2)+1); } ch=getch(obj); if(ch<=' ') break; } obj->yytext[len]=0; /* terminator */ if(is_string) { obj->yylen=len; return(T_STRING); } for(i=0;iyytext+1,tokens[i])) { return(i); } } return(T_UNKNOWN_KEY); } static int get_vartoken_patched(struct globals *obj) { int ch; int i, len=0; if(!obj->var_prevch) { for(;;) { ch=getch_patched(obj); if(ch<0) { free_2(obj->varsplit); obj->varsplit=NULL; return(V_END); } if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')) continue; break; } } else { ch=obj->var_prevch; obj->var_prevch=0; } if(ch=='[') return(V_LB); if(ch==':') return(V_COLON); if(ch==']') return(V_RB); for(obj->yytext[len++]=ch;;obj->yytext[len++]=ch) { if(len==obj->T_MAX_STR) { obj->yytext=(char *)realloc_2(obj->yytext, (obj->T_MAX_STR=obj->T_MAX_STR*2)+1); } ch=getch_patched(obj); if(ch<0) break; if((ch==':')||(ch==']')) { obj->var_prevch=ch; break; } } obj->yytext[len]=0; /* terminator */ for(i=0;iyytext,vartypes[i])) { if(ch<0) { free_2(obj->varsplit); obj->varsplit=NULL; } return(i); } } obj->yylen=len; if(ch<0) { free_2(obj->varsplit); obj->varsplit=NULL; } return(V_STRING); } static int get_vartoken(struct globals *obj) { int ch; int i, len=0; if(obj->varsplit) { int rc=get_vartoken_patched(obj); if(rc!=V_END) return(rc); obj->var_prevch=0; } if(!obj->var_prevch) { for(;;) { ch=getch(obj); if(ch<0) return(V_END); if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')) continue; break; } } else { ch=obj->var_prevch; obj->var_prevch=0; } if(ch=='[') return(V_LB); if(ch==':') return(V_COLON); if(ch==']') return(V_RB); for(obj->yytext[len++]=ch;;obj->yytext[len++]=ch) { if(len==obj->T_MAX_STR) { obj->yytext=(char *)realloc_2(obj->yytext, (obj->T_MAX_STR=obj->T_MAX_STR*2)+1); } ch=getch(obj); if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')||(ch<0)) break; if((ch=='[')&&(obj->yytext[0]!='\\')) { obj->varsplit=obj->yytext+len; /* keep looping so we get the *last* one */ } else if(((ch==':')||(ch==']'))&&(!obj->varsplit)&&(obj->yytext[0]!='\\')) { obj->var_prevch=ch; break; } } obj->yytext[len]=0; /* absolute terminator */ if((obj->varsplit)&&(obj->yytext[len-1]==']')) { char *vst; vst=malloc_2(strlen(obj->varsplit)+1); strcpy(vst, obj->varsplit); *obj->varsplit=0x00; /* zero out var name at the left bracket */ len=obj->varsplit-obj->yytext; obj->varsplit=obj->vsplitcurr=vst; obj->var_prevch=0; } else { obj->varsplit=NULL; } for(i=0;iyytext,vartypes[i])) { return(i); } } obj->yylen=len; return(V_STRING); } static int get_strtoken(struct globals *obj) { int ch; int len=0; if(!obj->var_prevch) { for(;;) { ch=getch(obj); if(ch<0) return(V_END); if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')) continue; break; } } else { ch=obj->var_prevch; obj->var_prevch=0; } for(obj->yytext[len++]=ch;;obj->yytext[len++]=ch) { if(len==obj->T_MAX_STR) { obj->yytext=(char *)realloc_2(obj->yytext, (obj->T_MAX_STR=obj->T_MAX_STR*2)+1); } ch=getch(obj); if((ch==' ')||(ch=='\t')||(ch=='\n')||(ch=='\r')||(ch<0)) break; } obj->yytext[len]=0; /* terminator */ obj->yylen=len; return(V_STRING); } static void sync_end(struct globals *obj, char *hdr) { int tok; if(hdr) DEBUG(fprintf(stderr,"%s",hdr)); for(;;) { tok=get_token(obj); if((tok==T_END)||(tok==T_EOF)) break; if(hdr)DEBUG(fprintf(stderr," %s",yytext)); } if(hdr) DEBUG(fprintf(stderr,"\n")); } static char *build_slisthier(struct globals *obj) { struct slist *s; int len=0; if(!obj->slistroot) { if(obj->slisthier) { free_2(obj->slisthier); } obj->slisthier_len=0; obj->slisthier=(char *)malloc_2(1); *obj->slisthier=0; return(obj->slisthier); } s=obj->slistroot; len=0; while(s) { len+=s->len+(s->next?1:0); s=s->next; } obj->slisthier=(char *)malloc_2((obj->slisthier_len=len)+1); s=obj->slistroot; len=0; while(s) { strcpy(obj->slisthier+len,s->str); len+=s->len; if(s->next) { strcpy(obj->slisthier+len,obj->vcd_hier_delimeter); len++; } s=s->next; } return(obj->slisthier); } void append_vcd_slisthier(struct globals *obj, char *str) { struct slist *s; s=(struct slist *)calloc_2(1,sizeof(struct slist)); s->len=strlen(str); s->str=(char *)malloc_2(s->len+1); strcpy(s->str,str); if(obj->slistcurr) { obj->slistcurr->next=s; obj->slistcurr=s; } else { obj->slistcurr=obj->slistroot=s; } build_slisthier(obj); DEBUG(fprintf(stderr, "SCOPE: %s\n",obj->slisthier)); } static void parse_valuechange(struct globals *obj) { struct vcdsymbol *v; char *vector; int vlen; switch(obj->yytext[0]) { case '0': case '1': case 'x': case 'X': case 'z': case 'Z': if(obj->yylen>1) { v=bsearch_vcd(obj, obj->yytext+1); if(!v) { fprintf(stderr,"Near byte %d, Unknown VCD identifier: '%s'\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf),obj->yytext+1); } else { if(v->vartype!=V_EVENT) { v->value[0]=obj->yytext[0]; DEBUG(fprintf(stderr,"%s = '%c'\n",v->name,v->value[0])); add_histent(obj, obj->current_time,v->narray[0],v->value[0],1, NULL); } else { v->value[0]=(obj->dumping_off)?'x':'1'; /* only '1' is relevant */ if(obj->current_time!=(v->ev->last_event_time+1)) { /* dump degating event */ DEBUG(fprintf(stderr,"#"TTFormat" %s = '%c' (event)\n",v->ev->last_event_time+1,v->name,'0')); add_histent(obj, v->ev->last_event_time+1,v->narray[0],'0',1, NULL); } DEBUG(fprintf(stderr,"%s = '%c' (event)\n",v->name,v->value[0])); add_histent(obj, obj->current_time,v->narray[0],v->value[0],1, NULL); v->ev->last_event_time=obj->current_time; } } } else { fprintf(stderr,"Near byte %d, Malformed VCD identifier\n", obj->vcdbyteno+(obj->vst-obj->vcdbuf)); } break; case 'b': case 'B': { /* extract binary number then.. */ vector=malloc_2(obj->yylen_cache=obj->yylen); strcpy(vector,obj->yytext+1); vlen=obj->yylen-1; get_strtoken(obj); v=bsearch_vcd(obj, obj->yytext); if(!v) { fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf), obj->yytext); free_2(vector); } else { if((obj->convert_to_reals)&&(v->vartype==V_REAL)) { double *d; char *pnt; char ch; TimeType k=0; pnt=vector; while((ch=*(pnt++))) { k=(k<<1)|((ch=='1')?1:0); } free_2(vector); d=malloc_2(sizeof(double)); *d=(double)k; if(!v) { fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf), obj->yytext); free_2(d); } else { add_histent(obj, obj->current_time, v->narray[0],'g',1,(char *)d); } break; } if(vlensize) /* fill in left part */ { char extend; int i, fill; extend=(vector[0]=='1')?'0':vector[0]; fill=v->size-vlen; for(i=0;ivalue[i]=extend; } strcpy(v->value+fill,vector); } else if(vlen==v->size) /* straight copy */ { strcpy(v->value,vector); } else /* too big, so copy only right half */ { int skip; skip=vlen-v->size; strcpy(v->value,vector+skip); } DEBUG(fprintf(stderr,"%s = '%s'\n",v->name, v->value)); if((v->size==1)||(!obj->atomic_vectors)) { int i; for(i=0;isize;i++) { add_histent(obj, obj->current_time, v->narray[i],v->value[i],1, NULL); } free_2(vector); } else { if(obj->yylen_cache!=(v->size+1)) { free_2(vector); vector=malloc_2(v->size+1); } strcpy(vector,v->value); add_histent(obj, obj->current_time, v->narray[0],0,1,vector); } } break; } case 'p': /* extract port dump value.. */ vector=malloc_2(obj->yylen_cache=obj->yylen); strcpy(vector,obj->yytext+1); vlen=obj->yylen-1; get_strtoken(obj); /* throw away 0_strength_component */ get_strtoken(obj); /* throw away 0_strength_component */ get_strtoken(obj); /* this is the id */ v=bsearch_vcd(obj, obj->yytext); if(!v) { fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf), obj->yytext); free_2(vector); } else { if((obj->convert_to_reals)&&(v->vartype==V_REAL)) /* should never happen, but just in case.. */ { double *d; char *pnt; char ch; TimeType k=0; pnt=vector; while((ch=*(pnt++))) { k=(k<<1)|((ch=='1')?1:0); } free_2(vector); d=malloc_2(sizeof(double)); *d=(double)k; if(!v) { fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf), obj->yytext); free_2(d); } else { add_histent(obj, obj->current_time, v->narray[0],'g',1,(char *)d); } break; } if(vlensize) /* fill in left part */ { char extend; int i, fill; extend='0'; fill=v->size-vlen; for(i=0;ivalue[i]=extend; } evcd_strcpy(v->value+fill,vector); } else if(vlen==v->size) /* straight copy */ { evcd_strcpy(v->value,vector); } else /* too big, so copy only right half */ { int skip; skip=vlen-v->size; evcd_strcpy(v->value,vector+skip); } DEBUG(fprintf(stderr,"%s = '%s'\n",v->name, v->value)); if((v->size==1)||(!obj->atomic_vectors)) { int i; for(i=0;isize;i++) { add_histent(obj, obj->current_time, v->narray[i],v->value[i],1, NULL); } free_2(vector); } else { if(obj->yylen_cachesize) { free_2(vector); vector=malloc_2(v->size+1); } strcpy(vector,v->value); add_histent(obj, obj->current_time, v->narray[0],0,1,vector); } } break; case 'r': case 'R': { double *d; d=malloc_2(sizeof(double)); sscanf(obj->yytext+1,"%lg",d); get_strtoken(obj); v=bsearch_vcd(obj, obj->yytext); if(!v) { fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf), obj->yytext); free_2(d); } else { add_histent(obj, obj->current_time, v->narray[0],'g',1,(char *)d); } break; } #ifndef STRICT_VCD_ONLY case 's': case 'S': { char *d; d=(char *)malloc_2(obj->yylen); strcpy(d, obj->yytext+1); get_strtoken(obj); v=bsearch_vcd(obj, obj->yytext); if(!v) { fprintf(stderr,"Near byte %d, Unknown identifier: '%s'\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf), obj->yytext); free_2(d); } else { add_histent(obj, obj->current_time, v->narray[0],'s',1,(char *)d); } break; } #endif } } static void evcd_strcpy(char *dst, char *src) { static char *evcd="DUNZduLHXTlh01?FAaBbCcf"; static char *vcd="01xz0101xz0101xzxxxxxxx"; char ch; int i; while((ch=*src)) { for(i=0;i<23;i++) { if(evcd[i]==ch) { *dst=vcd[i]; break; } } if(i==23) *dst='x'; src++; dst++; } *dst=0; /* null terminate destination */ } static void vcd_parse(struct globals *obj) { int tok; for(;;) { switch(tok=get_token(obj)) { case T_COMMENT: sync_end(obj, "COMMENT:"); break; case T_DATE: sync_end(obj, "DATE:"); break; case T_VERSION: sync_end(obj, "VERSION:"); break; case T_TIMESCALE: { int vtok; int i; char prefix=' '; vtok=get_token(obj); if((vtok==T_END)||(vtok==T_EOF)) break; obj->time_scale=atoi_64(obj->yytext); if(!obj->time_scale) obj->time_scale=1; for(i=0;iyylen;i++) { if((obj->yytext[i]<'0')||(obj->yytext[i]>'9')) { prefix=obj->yytext[i]; break; } } if(prefix==' ') { vtok=get_token(obj); if((vtok==T_END)||(vtok==T_EOF)) break; prefix=obj->yytext[0]; } switch(prefix) { case ' ': case 'm': case 'u': case 'n': case 'p': case 'f': obj->time_dimension=prefix; break; case 's': obj->time_dimension=' '; break; default: /* unknown */ obj->time_dimension='n'; break; } DEBUG(fprintf(stderr,"TIMESCALE: "TTFormat" %cs\n",obj->time_scale, obj->time_dimension)); sync_end(obj, NULL); } break; case T_SCOPE: T_GET(obj); T_GET(obj); if(tok==T_STRING) { struct slist *s; s=(struct slist *)calloc_2(1,sizeof(struct slist)); s->len=obj->yylen; s->str=(char *)malloc_2(obj->yylen+1); strcpy(s->str,obj->yytext); if(obj->slistcurr) { obj->slistcurr->next=s; obj->slistcurr=s; } else { obj->slistcurr=obj->slistroot=s; } build_slisthier(obj); DEBUG(fprintf(stderr, "SCOPE: %s\n",obj->slisthier)); } sync_end(obj, NULL); break; case T_UPSCOPE: if(obj->slistroot) { struct slist *s; s=obj->slistroot; if(!s->next) { free_2(s->str); free_2(s); obj->slistroot=obj->slistcurr=NULL; } else for(;;) { if(!s->next->next) { free_2(s->next->str); free_2(s->next); s->next=NULL; obj->slistcurr=s; break; } s=s->next; } build_slisthier(obj); DEBUG(fprintf(stderr, "SCOPE: %s\n",obj->slisthier)); } sync_end(obj, NULL); break; case T_VAR: if((obj->header_over)&&(0)) { fprintf(stderr,"$VAR encountered after $ENDDEFINITIONS near byte %d. VCD is malformed, exiting.\n",obj->vcdbyteno+(obj->vst-obj->vcdbuf)); exit(VCD_FAIL); } else { int vtok; struct vcdsymbol *v=NULL; obj->var_prevch=0; obj->varsplit=NULL; vtok=get_vartoken(obj); if(vtok>V_PORT) goto bail; v=(struct vcdsymbol *)calloc_2(1,sizeof(struct vcdsymbol)); v->vartype=vtok; v->msi=v->lsi=obj->vcd_explicit_zero_subscripts; /* indicate [un]subscripted status */ if(vtok==V_PORT) { vtok=get_vartoken(obj); if(vtok==V_STRING) { v->size=atoi_64(obj->yytext); if(!v->size) v->size=1; } else if(vtok==V_LB) { vtok=get_vartoken(obj); if(vtok==V_END) goto err; if(vtok!=V_STRING) goto err; v->msi=atoi_64(obj->yytext); vtok=get_vartoken(obj); if(vtok==V_RB) { v->lsi=v->msi; v->size=1; } else { if(vtok!=V_COLON) goto err; vtok=get_vartoken(obj); if(vtok!=V_STRING) goto err; v->lsi=atoi_64(obj->yytext); vtok=get_vartoken(obj); if(vtok!=V_RB) goto err; if(v->msi>v->lsi) { v->size=v->msi-v->lsi+1; } else { v->size=v->lsi-v->msi+1; } } } else goto err; vtok=get_strtoken(obj); if(vtok==V_END) goto err; v->id=(char *)malloc_2(obj->yylen+1); strcpy(v->id, obj->yytext); vtok=get_vartoken(obj); if(vtok!=V_STRING) goto err; if(obj->slisthier_len) { v->name=(char *)malloc_2(obj->slisthier_len+1+obj->yylen+1); strcpy(v->name,obj->slisthier); strcpy(v->name+obj->slisthier_len,obj->vcd_hier_delimeter); strcpy(v->name+obj->slisthier_len+1,obj->yytext); } else { v->name=(char *)malloc_2(obj->yylen+1); strcpy(v->name,obj->yytext); } if(obj->pv) { if(!strcmp(obj->pv->name,v->name)) { obj->pv->chain=v; v->root=obj->rootv; if(obj->pv==obj->rootv) obj->pv->root=obj->rootv; } else { obj->rootv=v; } } else { obj->rootv=v; } obj->pv=v; } else /* regular vcd var, not an evcd port var */ { vtok=get_vartoken(obj); if(vtok==V_END) goto err; v->size=atoi_64(obj->yytext); if(!v->size) v->size=1; vtok=get_strtoken(obj); if(vtok==V_END) goto err; v->id=(char *)malloc_2(obj->yylen+1); strcpy(v->id, obj->yytext); vtok=get_vartoken(obj); if(vtok!=V_STRING) goto err; if(obj->slisthier_len) { v->name=(char *)malloc_2(obj->slisthier_len+1+obj->yylen+1); strcpy(v->name,obj->slisthier); strcpy(v->name+obj->slisthier_len,obj->vcd_hier_delimeter); strcpy(v->name+obj->slisthier_len+1,obj->yytext); } else { v->name=(char *)malloc_2(obj->yylen+1); strcpy(v->name,obj->yytext); } if(obj->pv) { if(!strcmp(obj->pv->name,v->name)) { obj->pv->chain=v; v->root=obj->rootv; if(obj->pv==obj->rootv) obj->pv->root=obj->rootv; } else { obj->rootv=v; } } else { obj->rootv=v; } obj->pv=v; vtok=get_vartoken(obj); if(vtok==V_END) goto dumpv; if(vtok!=V_LB) goto err; vtok=get_vartoken(obj); if(vtok!=V_STRING) goto err; v->msi=atoi_64(obj->yytext); vtok=get_vartoken(obj); if(vtok==V_RB) { v->lsi=v->msi; goto dumpv; } if(vtok!=V_COLON) goto err; vtok=get_vartoken(obj); if(vtok!=V_STRING) goto err; v->lsi=atoi_64(obj->yytext); vtok=get_vartoken(obj); if(vtok!=V_RB) goto err; } dumpv: if((v->vartype==V_REAL)||((obj->convert_to_reals)&&((v->vartype==V_INTEGER)||(v->vartype==V_PARAMETER)))) { v->vartype=V_REAL; v->size=1; /* override any data we parsed in */ v->msi=v->lsi=0; } else if((v->size>1)&&(v->msi<=0)&&(v->lsi<=0)) { if(v->vartype==V_EVENT) { v->size=1; } else { /* any criteria for the direction here? */ v->msi=v->size-1; v->lsi=0; } } else if((v->msi>v->lsi)&&((v->msi-v->lsi+1)!=v->size)) { if((v->vartype!=V_EVENT)&&(v->vartype!=V_PARAMETER)) goto err; v->size=v->msi-v->lsi+1; } else if((v->lsi>=v->msi)&&((v->lsi-v->msi+1)!=v->size)) { if((v->vartype!=V_EVENT)&&(v->vartype!=V_PARAMETER)) goto err; v->size=v->msi-v->lsi+1; } /* initial conditions */ v->value=(char *)malloc_2(v->size+1); v->value[v->size]=0; v->narray=(struct Node **)calloc_2(v->size,sizeof(struct Node *)); { int i; if(obj->atomic_vectors) { for(i=0;isize;i++) { v->value[i]='x'; } v->narray[0]=(struct Node *)calloc_2(1,sizeof(struct Node)); v->narray[0]->head.time=-1; v->narray[0]->head.v.val=1; } else { for(i=0;isize;i++) { v->value[i]='x'; v->narray[i]=(struct Node *)calloc_2(1,sizeof(struct Node)); v->narray[i]->head.time=-1; v->narray[i]->head.v.val=1; } } } if(v->vartype==V_EVENT) { struct queuedevent *q; v->ev=q=(struct queuedevent *)calloc_2(1,sizeof(struct queuedevent)); q->sym=v; q->last_event_time=-1; q->next=queuedevents; queuedevents=q; } if(!obj->vcdsymroot) { obj->vcdsymroot=obj->vcdsymcurr=v; } else { obj->vcdsymcurr->next=v; obj->vcdsymcurr=v; } obj->numsyms++; DEBUG(fprintf(stderr,"VAR %s %d %s %s[%d:%d]\n", vartypes[v->vartype], v->size, v->id, v->name, v->msi, v->lsi)); goto bail; err: if(v) { if(v->name) free_2(v->name); if(v->id) free_2(v->id); if(v->value) free_2(v->value); free_2(v); } bail: if(vtok!=V_END) sync_end(obj, NULL); break; } case T_ENDDEFINITIONS: obj->header_over=1; /* do symbol table management here */ create_sorted_table(obj); if(!obj->sorted) { fprintf(stderr, "No symbols in VCD file..nothing to do!\n"); exit(VCD_FAIL); } break; case T_STRING: if(obj->header_over) { /* catchall for events when header over */ if(obj->yytext[0]=='#') { TimeType time; time=atoi_64(obj->yytext+1); if(obj->start_time<0) { obj->start_time=time; } obj->current_time=time; if(obj->end_timeend_time=time; /* in case of malformed vcd files */ DEBUG(fprintf(stderr,"#"TTFormat"\n",time)); } else { parse_valuechange(obj); } } break; case T_DUMPALL: /* dump commands modify vals anyway so */ case T_DUMPPORTSALL: break; /* just loop through.. */ case T_DUMPOFF: case T_DUMPPORTSOFF: obj->dumping_off=1; break; case T_DUMPON: case T_DUMPPORTSON: obj->dumping_off=0; break; case T_DUMPVARS: case T_DUMPPORTS: break; case T_VCDCLOSE: break; /* next token will be '#' time related followed by $end */ case T_END: /* either closure for dump commands or */ break; /* it's spurious */ case T_UNKNOWN_KEY: sync_end(obj, NULL); /* skip over unknown keywords */ break; case T_EOF: return; default: DEBUG(fprintf(stderr,"UNKNOWN TOKEN\n")); } } } /*******************************************************************************/ void add_histent(struct globals *obj, TimeType time, struct Node *n, char ch, int regadd, char *vector) { struct HistEnt *he; char heval; if(!vector) { if(!n->curr) { he=histent_calloc(obj); he->time=-1; he->v.val=1; n->curr=he; n->head.next=he; add_histent(obj, time,n,ch,regadd, vector); } else { if(regadd) { time*=(obj->time_scale); } if(ch=='0') heval=0; else if(ch=='1') heval=3; else if((ch=='x')||(ch=='X')) heval=1; else heval=2; if((n->curr->v.val!=heval)||(time==obj->start_time)) /* same region == go skip */ { if((n->curr->time==time)&&(obj->count_glitches)) { DEBUG(printf("Warning: Glitch at time ["TTFormat"] Signal [%p], Value [%c->%c].\n", time, n, "0XZ1"[n->curr->v.val], ch)); n->curr->v.val=heval; /* we have a glitch! */ obj->num_glitches++; if(!(n->curr->flags&HIST_GLITCH)) { n->curr->flags|=HIST_GLITCH; /* set the glitch flag */ obj->num_glitch_regions++; } } else { he=histent_calloc(obj); he->time=time; he->v.val=heval; n->curr->next=he; n->curr=he; obj->regions+=regadd; } } } } else { switch(ch) { case 's': /* string */ { if(!n->curr) { he=histent_calloc(obj); he->flags=(HIST_STRING|HIST_REAL); he->time=-1; he->v.vector=NULL; n->curr=he; n->head.next=he; add_histent(obj, time,n,ch,regadd, vector); } else { if(regadd) { time*=(obj->time_scale); } if((n->curr->time==time)&&(obj->count_glitches)) { DEBUG(printf("Warning: String Glitch at time ["TTFormat"] Signal [%p].\n", time, n)); if(n->curr->v.vector) free_2(n->curr->v.vector); n->curr->v.vector=vector; /* we have a glitch! */ obj->num_glitches++; if(!(n->curr->flags&HIST_GLITCH)) { n->curr->flags|=HIST_GLITCH; /* set the glitch flag */ obj->num_glitch_regions++; } } else { he=histent_calloc(obj); he->flags=(HIST_STRING|HIST_REAL); he->time=time; he->v.vector=vector; n->curr->next=he; n->curr=he; obj->regions+=regadd; } } break; } case 'g': /* real number */ { if(!n->curr) { he=histent_calloc(obj); he->flags=HIST_REAL; he->time=-1; he->v.vector=NULL; n->curr=he; n->head.next=he; add_histent(obj, time,n,ch,regadd, vector); } else { if(regadd) { time*=(obj->time_scale); } if( (n->curr->v.vector&&vector&&(*(double *)n->curr->v.vector!=*(double *)vector)) ||(time==obj->start_time) ||(!n->curr->v.vector) ) /* same region == go skip */ { if((n->curr->time==time)&&(obj->count_glitches)) { DEBUG(printf("Warning: Real number Glitch at time ["TTFormat"] Signal [%p].\n", time, n)); if(n->curr->v.vector) free_2(n->curr->v.vector); n->curr->v.vector=vector; /* we have a glitch! */ obj->num_glitches++; if(!(n->curr->flags&HIST_GLITCH)) { n->curr->flags|=HIST_GLITCH; /* set the glitch flag */ obj->num_glitch_regions++; } } else { he=histent_calloc(obj); he->flags=HIST_REAL; he->time=time; he->v.vector=vector; n->curr->next=he; n->curr=he; obj->regions+=regadd; } } else { free_2(vector); } } break; } default: { if(!n->curr) { he=histent_calloc(obj); he->time=-1; he->v.vector=NULL; n->curr=he; n->head.next=he; add_histent(obj, time,n,ch,regadd, vector); } else { if(regadd) { time*=(obj->time_scale); } if( (n->curr->v.vector&&vector&&(strcmp(n->curr->v.vector,vector))) ||(time==obj->start_time) ||(!n->curr->v.vector) ) /* same region == go skip */ { if((n->curr->time==time)&&(obj->count_glitches)) { DEBUG(printf("Warning: Glitch at time ["TTFormat"] Signal [%p], Value [%c->%c].\n", time, n, "0XZ1"[n->curr->v.val], ch)); if(n->curr->v.vector) free_2(n->curr->v.vector); n->curr->v.vector=vector; /* we have a glitch! */ obj->num_glitches++; if(!(n->curr->flags&HIST_GLITCH)) { n->curr->flags|=HIST_GLITCH; /* set the glitch flag */ obj->num_glitch_regions++; } } else { he=histent_calloc(obj); he->time=time; he->v.vector=vector; n->curr->next=he; n->curr=he; obj->regions+=regadd; } } else { free_2(vector); } } break; } } } } static void add_tail_histents(struct globals *obj) { int i,j; /* dump out any pending events 1st */ struct queuedevent *q; q=queuedevents; while(q) { struct vcdsymbol *v; v=q->sym; if(obj->current_time!=(v->ev->last_event_time+1)) { /* dump degating event */ DEBUG(fprintf(stderr,"#"TTFormat" %s = '%c' (event)\n",v->ev->last_event_time+1,v->name,'0')); add_histent(obj, v->ev->last_event_time+1,v->narray[0],'0',1, NULL); } q=q->next; } /* then do 'x' trailers */ for(i=0;inumsyms;i++) { struct vcdsymbol *v; v=obj->sorted[i]; if(v->vartype==V_REAL) { double *d; d=malloc_2(sizeof(double)); *d=1.0; add_histent(obj, MAX_HISTENT_TIME-1, v->narray[0], 'g', 0, (char *)d); } else if((v->size==1)||(!obj->atomic_vectors)) for(j=0;jsize;j++) { add_histent(obj, MAX_HISTENT_TIME-1, v->narray[j], 'x', 0, NULL); } else { add_histent(obj, MAX_HISTENT_TIME-1, v->narray[0], 'x', 0, (char *)calloc_2(1,sizeof(char))); } } for(i=0;inumsyms;i++) { struct vcdsymbol *v; v=obj->sorted[i]; if(v->vartype==V_REAL) { double *d; d=malloc_2(sizeof(double)); *d=0.0; add_histent(obj, MAX_HISTENT_TIME, v->narray[0], 'g', 0, (char *)d); } else if((v->size==1)||(!obj->atomic_vectors)) for(j=0;jsize;j++) { add_histent(obj, MAX_HISTENT_TIME, v->narray[j], 'z', 0, NULL); } else { add_histent(obj, MAX_HISTENT_TIME, v->narray[0], 'z', 0, (char *)calloc_2(1,sizeof(char))); } } } /*******************************************************************************/ static void vcd_build_symbols(struct globals *obj) { int i,j; int max_slen=-1; struct sym_chain *sym_chain=NULL, *sym_curr=NULL; for(i=0;inumsyms;i++) { struct vcdsymbol *v, *vprime; int msi; int delta; { char *str; int slen; int substnode; v=obj->sorted[i]; msi=v->msi; delta=((v->lsi-v->msi)<0)?-1:1; substnode=0; slen=strlen(v->name); str=(slen>max_slen)?(wave_alloca((max_slen=slen)+32)):(str); /* more than enough */ strcpy(str,v->name); if(v->msi>=0) { strcpy(str+slen,obj->vcd_hier_delimeter); slen++; } if((vprime=bsearch_vcd(obj, v->id))!=v) /* hash mish means dup net */ { if(v->size!=vprime->size) { fprintf(stderr,"ERROR: Duplicate IDs with differing width: %s %s\n", v->name, vprime->name); } else { substnode=1; } } if(((v->size==1)||(!obj->atomic_vectors))&&(v->vartype!=V_REAL)) { struct symbol *s; for(j=0;jsize;j++) { if(v->msi>=0) { if(!obj->vcd_explicit_zero_subscripts) sprintf(str+slen,"%d",msi); else sprintf(str+slen-1,"[%d]",msi); } if(!symfind(obj, str)) { s=symadd(obj, str,hash(str)); s->n=v->narray[j]; if(substnode) { struct Node *n, *n2; n=s->n; n2=vprime->narray[j]; /* nname stays same */ n->head=n2->head; n->curr=n2->curr; /* harray calculated later */ n->numhist=n2->numhist; } s->n->nname=s->name; s->h=s->n->curr; if(!obj->firstnode) { obj->firstnode=obj->curnode=s; } else { obj->curnode->nextinaet=s; obj->curnode=s; } obj->numfacs++; DEBUG(fprintf(stderr,"Added: %s\n",str)); } else { fprintf(stderr,"Warning: %s is a duplicate net name.\n",str); } msi+=delta; } if((j==1)&&(v->root)) { s->vec_root=(struct symbol *)v->root; /* these will get patched over */ s->vec_chain=(struct symbol *)v->chain; /* these will get patched over */ v->sym_chain=s; if(!sym_chain) { sym_curr=(struct sym_chain *)calloc_2(1,sizeof(struct sym_chain)); sym_chain=sym_curr; } else { sym_curr->next=(struct sym_chain *)calloc_2(1,sizeof(struct sym_chain)); sym_curr=sym_curr->next; } sym_curr->val=s; } } else /* atomic vector */ { if(v->vartype!=V_REAL) { sprintf(str+slen-1,"[%d:%d]",v->msi,v->lsi); } else { *(str+slen-1)=0; } if(!symfind(obj, str)) { struct symbol *s; s=symadd(obj, str,hash(str)); s->n=v->narray[0]; if(substnode) { struct Node *n, *n2; n=s->n; n2=vprime->narray[0]; /* nname stays same */ n->head=n2->head; n->curr=n2->curr; /* harray calculated later */ n->numhist=n2->numhist; n->ext=n2->ext; } else { struct ExtNode *en; en=(struct ExtNode *)malloc_2(sizeof(struct ExtNode)); en->msi=v->msi; en->lsi=v->lsi; s->n->ext=en; } s->n->nname=s->name; s->h=s->n->curr; if(!obj->firstnode) { obj->firstnode=obj->curnode=s; } else { obj->curnode->nextinaet=s; obj->curnode=s; } obj->numfacs++; DEBUG(fprintf(stderr,"Added: %s\n",str)); } else { fprintf(stderr,"Warning: %s is a duplicate net name.\n",str); } } } } if(sym_chain) { sym_curr=sym_chain; while(sym_curr) { sym_curr->val->vec_root= ((struct vcdsymbol *)sym_curr->val->vec_root)->sym_chain; if ((struct vcdsymbol *)sym_curr->val->vec_chain) sym_curr->val->vec_chain=((struct vcdsymbol *)sym_curr->val->vec_chain)->sym_chain; DEBUG(printf("Link: ('%s') '%s' -> '%s'\n",sym_curr->val->vec_root->name, sym_curr->val->name, sym_curr->val->vec_chain?sym_curr->val->vec_chain->name:"(END)")); sym_chain=sym_curr; sym_curr=sym_curr->next; free_2(sym_chain); } } } /*******************************************************************************/ void vcd_sortfacs(struct globals *obj) { int i; obj->facs=(struct symbol **)malloc_2(obj->numfacs*sizeof(struct symbol *)); obj->curnode=obj->firstnode; for(i=0;inumfacs;i++) { char *subst, ch; int len; obj->facs[i]=obj->curnode; if((len=strlen(subst=obj->curnode->name))>obj->longestname) obj->longestname=len; obj->curnode=obj->curnode->nextinaet; while((ch=(*subst))) { if(ch==obj->hier_delimeter) { *subst=0x01; } /* forces sort at hier boundaries */ subst++; } } quicksort(obj->facs,0,obj->numfacs-1); for(i=0;inumfacs;i++) { char *subst, ch; subst=obj->facs[i]->name; while((ch=(*subst))) { if(ch==0x01) { *subst=obj->hier_delimeter; } /* restore back to normal */ subst++; } #ifdef DEBUG_FACILITIES printf("%-4d %s\n",i,obj->facs[i]->name); #endif } obj->facs_are_sorted=1; } /*******************************************************************************/ static void vcd_cleanup(struct globals *obj) { int i; struct slist *s, *s2; if(obj->sorted) { for(i=0;inumsyms;i++) { struct vcdsymbol *v; v=obj->sorted[i]; if(v) { if(v->name) free_2(v->name); if(v->id) free_2(v->id); if(v->value) free_2(v->value); if(v->ev) free_2(v->ev); if(v->narray) free_2(v->narray); free_2(v); } } free_2(obj->sorted); obj->sorted=NULL; } if(obj->slisthier) { free_2(obj->slisthier); obj->slisthier=NULL; } s=obj->slistroot; while(s) { s2=s->next; if(s->str)free_2(s->str); free_2(s); s=s2; } obj->slistroot=obj->slistcurr=NULL; obj->slisthier_len=0; queuedevents=NULL; /* deallocated in the symbol stuff */ if(obj->vcd_is_compressed) { pclose(obj->vcd_handle); } else { fclose(obj->vcd_handle); } if(obj->yytext) { free_2(obj->yytext); obj->yytext=NULL; } } /*******************************************************************************/ TimeType vcd_main(struct globals *obj, char *fname) { int flen; obj->pv=obj->rootv=NULL; obj->vcd_hier_delimeter[0]=obj->hier_delimeter; errno=0; /* reset in case it's set for some reason */ printf("Processing '%s'\n",fname); obj->yytext=(char *)malloc_2(obj->T_MAX_STR+1); flen=strlen(fname); if (((flen>2)&&(!strcmp(fname+flen-3,".gz")))|| ((flen>3)&&(!strcmp(fname+flen-4,".zip")))) { char *str; int dlen; dlen=strlen(WAVE_DECOMPRESSOR); str=wave_alloca(strlen(fname)+dlen+1); strcpy(str,WAVE_DECOMPRESSOR); strcpy(str+dlen,fname); obj->vcd_handle=popen(str,"r"); obj->vcd_is_compressed=~0; } else { if(strcmp("-vcd",fname)) { obj->vcd_handle=fopen(fname,"rb"); } else { obj->vcd_handle=stdin; } obj->vcd_is_compressed=0; } if(!obj->vcd_handle) { fprintf(stderr, "Error opening %s .vcd file '%s'.\n", obj->vcd_is_compressed?"compressed":"", fname); exit(VCD_FAIL); } getch_alloc(obj); /* alloc membuff for vcd getch buffer */ build_slisthier(obj); vcd_parse(obj); add_tail_histents(obj); vcd_build_symbols(obj); vcd_sortfacs(obj); vcd_cleanup(obj); printf("Found %d symbols.\n", obj->numfacs); printf("["TTFormat"] start time.\n["TTFormat"] end time.\n", obj->start_time, obj->end_time); if(obj->num_glitches) printf("Warning: encountered %d glitch%s across %d glitch region%s.\n", obj->num_glitches, (obj->num_glitches!=1)?"es":"", obj->num_glitch_regions, (obj->num_glitch_regions!=1)?"s":""); getch_free(obj); /* free membuff for vcd getch buffer */ obj->min_time=obj->start_time*obj->time_scale; obj->max_time=obj->end_time*obj->time_scale; if(obj->min_time==obj->max_time) { fprintf(stderr, "VCD times range is equal to zero. Exiting.\n"); exit(VCD_FAIL); } return(obj->max_time); } /*******************************************************************************/