1986 lines
41 KiB
C
1986 lines
41 KiB
C
/*
|
|
* 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;i<NUM_TOKENS;i++)
|
|
{
|
|
if(!strcmp(obj->yytext+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;i<NUM_VTOKENS;i++)
|
|
{
|
|
if(!strcmp(obj->yytext,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;i<NUM_VTOKENS;i++)
|
|
{
|
|
if(!strcmp(obj->yytext,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(vlen<v->size) /* fill in left part */
|
|
{
|
|
char extend;
|
|
int i, fill;
|
|
|
|
extend=(vector[0]=='1')?'0':vector[0];
|
|
|
|
fill=v->size-vlen;
|
|
for(i=0;i<fill;i++)
|
|
{
|
|
v->value[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;i<v->size;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(vlen<v->size) /* fill in left part */
|
|
{
|
|
char extend;
|
|
int i, fill;
|
|
|
|
extend='0';
|
|
|
|
fill=v->size-vlen;
|
|
for(i=0;i<fill;i++)
|
|
{
|
|
v->value[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;i<v->size;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)
|
|
{
|
|
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;i<obj->yylen;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;i<v->size;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;i<v->size;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_time<time) obj->end_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;i<obj->numsyms;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;j<v->size;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;i<obj->numsyms;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;j<v->size;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;i<obj->numsyms;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;j<v->size;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;i<obj->numfacs;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;i<obj->numfacs;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;i<obj->numsyms;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);
|
|
}
|
|
|
|
/*******************************************************************************/
|