mirror of
https://github.com/steveicarus/iverilog.git
synced 2026-09-03 04:33:56 +02:00
By adding ivtest to the iverilog source tree, it is easier to keep the regression test synchronized with the source that is being tested. This should be especially helpful for PRs that add a new feature, and have a matching ivtest PR with the regression test for that feature.
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);
|
|
}
|
|
|
|
/*******************************************************************************/
|