diff --git a/vpi/Makefile.in b/vpi/Makefile.in index 4e02ee350..47c34d50a 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -18,7 +18,7 @@ # 59 Temple Place - Suite 330 # Boston, MA 02111-1307, USA # -#ident "$Id: Makefile.in,v 1.29 2001/09/20 03:21:02 steve Exp $" +#ident "$Id: Makefile.in,v 1.30 2002/03/09 21:54:48 steve Exp $" # # SHELL = /bin/sh @@ -57,6 +57,7 @@ all: system.vpi O = sys_table.o sys_deposit.o sys_display.o sys_finish.o sys_random.o \ sys_readmem.o sys_readmem_lex.o sys_time.o sys_vcd.o \ +sys_lxt.o lxt_write.o \ mt19937int.o SYSTEM_VPI_LDFLAGS = -L.. -lvpi diff --git a/vpi/lxt_write.c b/vpi/lxt_write.c new file mode 100644 index 000000000..691768d81 --- /dev/null +++ b/vpi/lxt_write.c @@ -0,0 +1,1528 @@ +/* + * Copyright (c) 2001 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "lxt_write.h" + +/* + * functions which emit various big endian + * data to a file + */ +static int lt_emit_u8(struct lt_trace *lt, int value) +{ +unsigned char buf[1]; +int nmemb; + +buf[0] = value & 0xff; +nmemb=fwrite(buf, sizeof(char), 1, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lt_emit_u16(struct lt_trace *lt, int value) +{ +unsigned char buf[2]; +int nmemb; + +buf[0] = (value>>8) & 0xff; +buf[1] = value & 0xff; +nmemb = fwrite(buf, sizeof(char), 2, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lt_emit_u24(struct lt_trace *lt, int value) +{ +unsigned char buf[3]; +int nmemb; + +buf[0] = (value>>16) & 0xff; +buf[1] = (value>>8) & 0xff; +buf[2] = value & 0xff; +nmemb=fwrite(buf, sizeof(char), 3, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lt_emit_u32(struct lt_trace *lt, int value) +{ +unsigned char buf[4]; +int nmemb; + +buf[0] = (value>>24) & 0xff; +buf[1] = (value>>16) & 0xff; +buf[2] = (value>>8) & 0xff; +buf[3] = value & 0xff; +nmemb=fwrite(buf, sizeof(char), 4, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lt_emit_u64(struct lt_trace *lt, int valueh, int valuel) +{ +int rc; + +if((rc=lt_emit_u32(lt, valueh))) + { + rc=lt_emit_u32(lt, valuel); + } + +return(rc); +} + + +static int lt_emit_double(struct lt_trace *lt, double value) +{ +int nmemb; + +nmemb=fwrite(&value, sizeof(char), sizeof(double)/sizeof(char), lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lt_emit_string(struct lt_trace *lt, char *value) +{ +int rc=1; +do + { + rc&=lt_emit_u8(lt, *value); + } while(*(value++)); +return(rc); +} + + +/* + * hash/symtable manipulation + */ +static int lt_hash(const char *s) +{ +const char *p; +char ch; +unsigned int h=0, h2=0, pos=0, g; +for(p=s;*p;p++) + { + ch=*p; + h2<<=3; + h2-=((unsigned int)ch+(pos++)); /* this handles stranded vectors quite well.. */ + + h=(h<<4)+ch; + if((g=h&0xf0000000)) + { + h=h^(g>>24); + h=h^g; + } + } + +h^=h2; /* combine the two hashes */ +return(h%LT_SYMPRIME); +} + + +static struct lt_symbol *lt_symadd(struct lt_trace *lt, const char *name, int hv) +{ +struct lt_symbol *s; + +s=(struct lt_symbol *)calloc(1,sizeof(struct lt_symbol)); +strcpy(s->name=(char *)malloc((s->namlen=strlen(name))+1),name); +s->next=lt->sym[hv]; +lt->sym[hv]=s; +return(s); +} + + +static struct lt_symbol *lt_symfind(struct lt_trace *lt, const char *s) +{ +int hv; +struct lt_symbol *temp; + +hv=lt_hash(s); +if(!(temp=lt->sym[hv])) return(NULL); /* no hash entry, add here wanted to add */ + +while(temp) + { + if(!strcmp(temp->name,s)) + { + return(temp); /* in table already */ + } + if(!temp->next) break; + temp=temp->next; + } + +return(NULL); /* not found, add here if you want to add*/ +} + + +/* + * compress facs to a prefix count + string + 0x00 + */ +static void lt_compress_fac(struct lt_trace *lt, char *str) +{ +int i; +int len = strlen(str); +int minlen = (lencompress_fac_len) ? len : lt->compress_fac_len; + +if(minlen>65535) minlen=65535; /* keep in printable range--most hierarchies won't be this big anyway */ + +if(lt->compress_fac_str) + { + for(i=0;icompress_fac_str[i]!=str[i]) break; + } + lt_emit_u16(lt, i); + lt_emit_string(lt, str+i); + free(lt->compress_fac_str); + } + else + { + lt_emit_u16(lt, 0); + lt_emit_string(lt, str); + } + +lt->compress_fac_str = (char *) malloc((lt->compress_fac_len=len)+1); +strcpy(lt->compress_fac_str, str); +} + + +/* + * emit facs in sorted order along with geometry + * and sync table info + */ +static int lt_compare(const void *v1, const void *v2) +{ +struct lt_symbol *s1 = *(struct lt_symbol **)v1; +struct lt_symbol *s2 = *(struct lt_symbol **)v2; +int rc = strcmp(s1->name, s2->name); +if(rc) + { + return(rc); + } + else + { + return(s1->msb - s2->msb); + } +} + + +static void strip_brack(struct lt_symbol *s) +{ +char *lastch = s->name+s->namlen - 1; +if(*lastch!=']') return; +if(s->namlen<3) return; +lastch--; +while(lastch!=s->name) + { + if(*lastch=='[') + { + *lastch=0x00; + return; + } + lastch--; + } +return; +} + + +static void lt_emitfacs(struct lt_trace *lt) +{ +int i; + +if((lt)&&(lt->numfacs)) + { + struct lt_symbol *s = lt->symchain; + if((lt->sorted_facs = (struct lt_symbol **)calloc(lt->numfacs, sizeof(struct lt_symbol *)))) + { + if(lt->do_strip_brackets) + for(i=0;inumfacs;i++) + { + lt->sorted_facs[i] = s; + strip_brack(s); + s=s->symchain; + } + else + for(i=0;inumfacs;i++) + { + lt->sorted_facs[i] = s; + s=s->symchain; + } + qsort((void *)lt->sorted_facs, lt->numfacs, sizeof(struct lt_symbol *), lt_compare); + lt->facname_offset=lt->position; + lt_emit_u32(lt, lt->numfacs); + lt_emit_u32(lt, lt->numfacbytes); + for(i=0;inumfacs;i++) + { + lt->sorted_facs[i]->facnum = i; + lt_compress_fac(lt, lt->sorted_facs[i]->name); + } + free(lt->compress_fac_str); lt->compress_fac_str=NULL; + lt->compress_fac_len=0; + + lt->facgeometry_offset = lt->position; + for(i=0;inumfacs;i++) + { + if((lt->sorted_facs[i]->flags<_SYM_F_ALIAS)==0) + { + lt_emit_u32(lt, lt->sorted_facs[i]->rows); + lt_emit_u32(lt, lt->sorted_facs[i]->msb); + lt_emit_u32(lt, lt->sorted_facs[i]->lsb); + lt_emit_u32(lt, lt->sorted_facs[i]->flags); + } + else + { + lt_emit_u32(lt, lt->sorted_facs[i]->aliased_to->facnum); + lt_emit_u32(lt, lt->sorted_facs[i]->msb); + lt_emit_u32(lt, lt->sorted_facs[i]->lsb); + lt_emit_u32(lt, LT_SYM_F_ALIAS); + } + } + + lt->sync_table_offset = lt->position; + for(i=0;inumfacs;i++) + { + lt_emit_u32(lt, lt->sorted_facs[i]->last_change); + } + } + } +} + + +/* + * initialize the trace and get back and lt context + */ +struct lt_trace *lt_init(const char *name) +{ +struct lt_trace *lt=(struct lt_trace *)calloc(1, sizeof(struct lt_trace)); + +if(!(lt->handle=fopen(name, "wb"))) + { + free(lt); + lt=NULL; + } + else + { + lt_emit_u16(lt, LT_HDRID); + lt_emit_u16(lt, LT_VERSION); + lt->change_field_offset = lt->position; + lt->initial_value = -1; /* if a user sets this it will have a different POSITIVE val */ + lt->timescale = -256; /* will be in range of -128<=x<=127 if set */ + } + +return(lt); +} + +/* + * clock flushing.. + */ +static void lt_flushclock(struct lt_trace *lt, struct lt_symbol *s) +{ +unsigned int last_change_delta = lt->position - s->last_change - 2; +unsigned int start_position = lt->position; +int tag; +int numbytes, numbytes_trans; +int numtrans = s->clk_numtrans - LT_CLKPACK - 1; + +if(numtrans<0) + { + /* it never got around to caching */ + fprintf(stderr, "Possible Problem with %s with %d?\n", s->name, s->clk_numtrans); + return; + } + +if(last_change_delta >= 256*65536) + { + numbytes = 3; + } +else +if(last_change_delta >= 65536) + { + numbytes = 2; + } +else +if(last_change_delta >= 256) + { + numbytes = 1; + } +else + { + numbytes = 0; + } + +if(numtrans >= 256*65536) + { + numbytes_trans = 3; + } +else +if(numtrans >= 65536) + { + numbytes_trans = 2; + } +else +if(numtrans >= 256) + { + numbytes_trans = 1; + } +else + { + numbytes_trans = 0; + } + +tag = (numbytes<<4) + 0xC + numbytes_trans; /* yields xC..xF */ + +lt_emit_u8(lt, tag); +switch(numbytes&3) + { + case 0: lt_emit_u8(lt, last_change_delta); break; + case 1: lt_emit_u16(lt, last_change_delta); break; + case 2: lt_emit_u24(lt, last_change_delta); break; + case 3: lt_emit_u32(lt, last_change_delta); break; + } + +s->last_change = start_position; + +/* s->clk_prevval CAN BE INFERRED! */ +/* s->clk_prevtrans CAN BE INFERRED! */ +/* s->clk_delta CAN BE INFERRED! */ + +switch(numbytes_trans&3) + { + case 0: lt_emit_u8(lt, numtrans); break; + case 1: lt_emit_u16(lt, numtrans); break; + case 2: lt_emit_u24(lt, numtrans); break; + case 3: lt_emit_u32(lt, numtrans); break; + } + +/* printf("Clock finish for '%s' at %d ending with '%c' for %d repeats over a switch delta of %d\n", + s->name, lt->timeval, s->clk_prevval, s->clk_numtrans - LT_CLKPACK, s->clk_delta); */ +s->clk_prevtrans = -1; +s->clk_numtrans = 0; +} + + +/* + * close out the trace and fixate it + */ +void lt_close(struct lt_trace *lt) +{ +int lasttime=0; +int lastposition=0; + +if(lt) + { + struct lt_symbol *s = lt->symchain; + + if(lt->clock_compress) + while(s) + { + if((s->clk_prevtrans!=-1)&&(s->clk_numtrans > LT_CLKPACK)) lt_flushclock(lt, s); + s=s->symchain; + } + + lt_emitfacs(lt); + + if(lt->timebuff) + { + free(lt->timebuff); + lt->timebuff=NULL; + } + if(lt->timehead) + { + struct lt_timetrail *t=lt->timehead; + struct lt_timetrail *t2; + + lt->time_table_offset = lt->position; + lt_emit_u32(lt, lt->timechangecount); + lt_emit_u32(lt, lt->mintime); + lt_emit_u32(lt, lt->maxtime); + while(t) + { + lt_emit_u32(lt, t->position - lastposition); lastposition = t->position; + t=t->next; + } + + t=lt->timehead; + while(t) + { + lt_emit_u32(lt, t->timeval - lasttime); lasttime = t->timeval; + + t2=t->next; + free(t); + t=t2; + } + + lt->timehead = lt->timecurr = NULL; + } + + if(lt->initial_value>=0) + { + lt->initial_value_offset = lt->position; + lt_emit_u8(lt, lt->initial_value); + } + + if((lt->timescale>-129)&(lt->timescale<128)) + { + lt->timescale_offset = lt->position; + lt_emit_u8(lt, lt->timescale); + } + + if(lt->double_used) + { + lt->double_test_offset = lt->position; + lt_emit_double(lt, 3.14159); + } + + lt_emit_u8(lt, LT_SECTION_END); + if(lt->change_field_offset) { lt_emit_u32(lt, lt->change_field_offset); lt_emit_u8(lt, LT_SECTION_CHG); } + if(lt->sync_table_offset) { lt_emit_u32(lt, lt->sync_table_offset); lt_emit_u8(lt, LT_SECTION_SYNC_TABLE); } + if(lt->facname_offset) { lt_emit_u32(lt, lt->facname_offset); lt_emit_u8(lt, LT_SECTION_FACNAME); } + if(lt->facgeometry_offset) { lt_emit_u32(lt, lt->facgeometry_offset); lt_emit_u8(lt, LT_SECTION_FACNAME_GEOMETRY); } + if(lt->timescale_offset) { lt_emit_u32(lt, lt->timescale_offset); lt_emit_u8(lt, LT_SECTION_TIMESCALE); } + if(lt->time_table_offset) { lt_emit_u32(lt, lt->time_table_offset); lt_emit_u8(lt, LT_SECTION_TIME_TABLE); } + if(lt->initial_value_offset) { lt_emit_u32(lt, lt->initial_value_offset); lt_emit_u8(lt, LT_SECTION_INITIAL_VALUE); } + if(lt->double_test_offset) { lt_emit_u32(lt, lt->double_test_offset); lt_emit_u8(lt, LT_SECTION_DOUBLE_TEST); } + lt_emit_u8(lt, LT_TRLID); + + if(lt->symchain) + { + struct lt_symbol *s = lt->symchain; + struct lt_symbol *s2; + + while(s) + { + free(s->name); + s2=s->next; + free(s); + s=s2; + } + } + + fclose(lt->handle); + free(lt); + } + +} + + +/* + * maint function for finding a symbol if it exists + */ +struct lt_symbol *lt_symbol_find(struct lt_trace *lt, const char *name) +{ +struct lt_symbol *s=NULL; + +if((lt)&&(name)) s=lt_symfind(lt, name); +return(s); +} + + +/* + * add a trace (if it doesn't exist already) + */ +struct lt_symbol *lt_symbol_add(struct lt_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags) +{ +struct lt_symbol *s; +int len; +int flagcnt; + +flagcnt = ((flags<_SYM_F_INTEGER)!=0) + ((flags<_SYM_F_DOUBLE)!=0) + ((flags<_SYM_F_STRING)!=0); + +if((flagcnt>1)||(!lt)||(!name)||(lt_symfind(lt, name))) return (NULL); + +lt->double_used |= ((flags<_SYM_F_DOUBLE)!=0); + +s=lt_symadd(lt, name, lt_hash(name)); +s->rows = rows; +s->flags = flags&(~LT_SYM_F_ALIAS); /* aliasing makes no sense here.. */ + +if(!flagcnt) + { + s->msb = msb; + s->lsb = lsb; + s->len = (msblen==1)&&(s->rows==0)) s->clk_prevtrans = -1; + } + +s->symchain = lt->symchain; +lt->symchain = s; +lt->numfacs++; +if((len=strlen(name)) > lt->longestname) lt->longestname = len; +lt->numfacbytes += (len+1); + +return(s); +} + +/* + * add an alias trace (if it doesn't exist already and orig is found) + */ +struct lt_symbol *lt_symbol_alias(struct lt_trace *lt, const char *existing_name, const char *alias, int msb, int lsb) +{ +struct lt_symbol *s, *sa; +int len; +int bitlen; +int flagcnt; + +if((!lt)||(!existing_name)||(!alias)||(!(s=lt_symfind(lt, existing_name)))||(lt_symfind(lt, alias))) return (NULL); + +while(s->aliased_to) /* find root alias */ + { + s=s->aliased_to; + } + +flagcnt = ((s->flags<_SYM_F_INTEGER)!=0) + ((s->flags<_SYM_F_DOUBLE)!=0) + ((s->flags<_SYM_F_STRING)!=0); +bitlen = (msblen)) return(NULL); + +sa=lt_symadd(lt, alias, lt_hash(alias)); +sa->flags = LT_SYM_F_ALIAS; /* only point this can get set */ +sa->aliased_to = s; + +if(!flagcnt) + { + sa->msb = msb; + sa->lsb = lsb; + sa->len = bitlen; + } + +sa->symchain = lt->symchain; +lt->symchain = sa; +lt->numfacs++; +if((len=strlen(alias)) > lt->longestname) lt->longestname = len; +lt->numfacbytes += (len+1); + +return(sa); +} + + +/* + * set current time + */ +int lt_set_time(struct lt_trace *lt, int timeval) +{ +int rc=0; + +if(timeval>=0) +if(lt) + { + struct lt_timetrail *trl=(struct lt_timetrail *)calloc(1, sizeof(struct lt_timetrail)); + if(trl) + { + trl->timeval = timeval; + trl->position = lt->position; + + if((lt->timecurr)||(lt->timebuff)) + { + if((timeval>lt->mintime)&&(timeval>lt->maxtime)) + { + lt->maxtime = timeval; + } + else + { + free(trl); + goto bail; + } + } + else + { + lt->mintime = lt->maxtime = timeval; + } + + if(lt->timebuff) + { + free(lt->timebuff); + } + lt->timebuff = trl; + lt->timeval = timeval; + rc=1; + } + } + +bail: +return(rc); +} + + +/* + * sets trace timescale as 10**x seconds + */ +void lt_set_timescale(struct lt_trace *lt, int timescale) +{ +if(lt) + { + lt->timescale = timescale; + } +} + + +/* + * sets clock compression heuristic + */ +void lt_set_clock_compress(struct lt_trace *lt) +{ +if(lt) + { + lt->clock_compress = 1; + } +} + + +/* + * sets trace initial value + */ +void lt_set_initial_value(struct lt_trace *lt, char value) +{ +if(lt) + { + int tag; + switch(value) + { + case '0': tag = 0; break; + case '1': tag = 1; break; + case 'Z': + case 'z': tag = 2; break; + case 'X': + case 'x': tag = 3; break; + case 'H': + case 'h': tag = 4; break; + case 'U': + case 'u': tag = 5; break; + case 'W': + case 'w': tag = 6; break; + case 'L': + case 'l': tag = 0x7; break; + case '-': tag = 0x8; break; + default: tag = -1; break; + } + lt->initial_value = tag; + } +} + + +/* + * Sets bracket stripping (useful for VCD conversions of + * bitblasted nets) + */ +void lt_symbol_bracket_stripping(struct lt_trace *lt, int doit) +{ +if(lt) + { + lt->do_strip_brackets = (doit!=0); + } +} + + +/* + * emission for trace values.. + */ +static int lt_optimask[]= +{ +0x00000000, + +0x00000001, +0x00000003, +0x00000007, +0x0000000f, + +0x0000001f, +0x0000003f, +0x0000007f, +0x000000ff, + +0x000001ff, +0x000003ff, +0x000007ff, +0x00000fff, + +0x00001fff, +0x00003fff, +0x00007fff, +0x0000ffff, + +0x0001ffff, +0x0003ffff, +0x0007ffff, +0x000fffff, + +0x001fffff, +0x003fffff, +0x007fffff, +0x00ffffff, + +0x01ffffff, +0x03ffffff, +0x07ffffff, +0x0fffffff, + +0x1fffffff, +0x3fffffff, +0x7fffffff, +0xffffffff +}; + + +int lt_emit_value_int(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, int value) +{ +int rc=0; + +if((!lt)||(!s)) return(rc); + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if(!(s->flags&(LT_SYM_F_DOUBLE|LT_SYM_F_STRING))) + { + int numbytes; /* number of bytes to store value minus one */ + int len = ((s->flags)<_SYM_F_INTEGER) ? 32 : s->len; + unsigned int last_change_delta; + + if(lt->clock_compress) + if((s->len==1)&&(s->rows==0)) /* possible clock handling */ + { + int ivalue = value&1; + + if(((s->clk_prevval == '1') && (ivalue==0)) || ((s->clk_prevval == '0') && (ivalue==1))) + { + if(s->clk_prevtrans==-1) + { + s->clk_prevtrans = lt->timeval; + s->clk_numtrans = 0; + } + else + if(s->clk_numtrans == 0) + { + s->clk_delta = lt->timeval - s->clk_prevtrans; + s->clk_prevtrans = lt->timeval; + s->clk_numtrans++; + } + else + { + if(s->clk_delta == (lt->timeval - s->clk_prevtrans)) + { + s->clk_numtrans++; + s->clk_prevtrans = lt->timeval; + if(s->clk_numtrans > LT_CLKPACK) + { + s->clk_prevval = ivalue + '0'; + + /* printf("Clock value '%d' for '%s' at %d (#%d)\n", ivalue, s->name, lt->timeval, s->clk_numtrans); */ + return(1); + } + } + else + { + if(s->clk_numtrans > LT_CLKPACK) + { + lt_flushclock(lt, s); + /* flush clock then continue below! */ + } + else + { + s->clk_prevtrans=-1; + } + } + } + + } + else + { + if(s->clk_numtrans > LT_CLKPACK) + { + lt_flushclock(lt, s); + /* flush clock then continue below! */ + } + else + { + s->clk_prevtrans=-1; + } + } + + s->clk_prevval = ivalue + '0'; + } + + /* normal trace handling */ + + last_change_delta = lt->position - s->last_change - 2; + + if(last_change_delta >= 256*65536) + { + numbytes = 3; + } + else + if(last_change_delta >= 65536) + { + numbytes = 2; + } + else + if(last_change_delta >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + if(len<=32) + { + int start_position = lt->position; + int tag; + int optimized0 = ((value<_optimask[len])==0); + int optimized1 = ((value<_optimask[len])==lt_optimask[len]); + int optimized = optimized0|optimized1; + + if(optimized) + { + tag = (numbytes<<4) | (3+optimized1); /* for x3 and x4 cases */ + } + else + { + tag = (numbytes<<4); + } + + lt_emit_u8(lt, tag); + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, last_change_delta); break; + case 1: lt_emit_u16(lt, last_change_delta); break; + case 2: lt_emit_u24(lt, last_change_delta); break; + case 3: lt_emit_u32(lt, last_change_delta); break; + } + + s->last_change = start_position; + + if(s->rows>0) + { + if(s->rows >= 256*65536) + { + numbytes = 3; + } + else + if(s->rows >= 65536) + { + numbytes = 2; + } + else + if(s->rows >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, row); break; + case 1: lt_emit_u16(lt, row); break; + case 2: lt_emit_u24(lt, row); break; + case 3: lt_emit_u32(lt, row); break; + } + } + + if(!optimized) + { + if(len<9) + { + value <<= (8-len); + rc=lt_emit_u8(lt, value); + } + else + if(len<17) + { + value <<= (16-len); + rc=lt_emit_u16(lt, value); + } + else + if(len<25) + { + value <<= (24-len); + rc=lt_emit_u24(lt, value); + } + else + { + value <<= (32-len); + rc=lt_emit_u32(lt, value); + } + } + } + + if(lt->timebuff) + { + lt->timechangecount++; + if(lt->timecurr) + { + lt->timecurr->next = lt->timebuff; + lt->timecurr = lt->timebuff; + } + else + { + lt->timehead = lt->timecurr = lt->timebuff; + } + + lt->timebuff=NULL; + } + } + +return(rc); +} + + +int lt_emit_value_double(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, double value) +{ +int rc=0; +int start_position; +int tag; + +if((!lt)||(!s)) return(rc); + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if((s->flags)<_SYM_F_DOUBLE) + { + int numbytes; /* number of bytes to store value minus one */ + unsigned int last_change_delta = lt->position - s->last_change - 2; + + if(last_change_delta >= 256*65536) + { + numbytes = 3; + } + else + if(last_change_delta >= 65536) + { + numbytes = 2; + } + else + if(last_change_delta >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + start_position = lt->position; + tag = (numbytes<<4); + lt_emit_u8(lt, tag); + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, last_change_delta); break; + case 1: lt_emit_u16(lt, last_change_delta); break; + case 2: lt_emit_u24(lt, last_change_delta); break; + case 3: lt_emit_u32(lt, last_change_delta); break; + } + + s->last_change = start_position; + + if(s->rows>0) + { + if(s->rows >= 256*65536) + { + numbytes = 3; + } + else + if(s->rows >= 65536) + { + numbytes = 2; + } + else + if(s->rows >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, row); break; + case 1: lt_emit_u16(lt, row); break; + case 2: lt_emit_u24(lt, row); break; + case 3: lt_emit_u32(lt, row); break; + } + } + + rc=lt_emit_double(lt, value); + + if(lt->timebuff) + { + lt->timechangecount++; + if(lt->timecurr) + { + lt->timecurr->next = lt->timebuff; + lt->timecurr = lt->timebuff; + } + else + { + lt->timehead = lt->timecurr = lt->timebuff; + } + + lt->timebuff=NULL; + } + } + +return(rc); +} + + +int lt_emit_value_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value) +{ +int rc=0; +int start_position; +int tag; + +if((!lt)||(!s)||(!value)) return(rc); + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if((s->flags)<_SYM_F_STRING) + { + int numbytes; /* number of bytes to store value minus one */ + unsigned int last_change_delta = lt->position - s->last_change - 2; + + if(last_change_delta >= 256*65536) + { + numbytes = 3; + } + else + if(last_change_delta >= 65536) + { + numbytes = 2; + } + else + if(last_change_delta >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + start_position = lt->position; + tag = (numbytes<<4); + lt_emit_u8(lt, tag); + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, last_change_delta); break; + case 1: lt_emit_u16(lt, last_change_delta); break; + case 2: lt_emit_u24(lt, last_change_delta); break; + case 3: lt_emit_u32(lt, last_change_delta); break; + } + + s->last_change = start_position; + + if(s->rows>0) + { + if(s->rows >= 256*65536) + { + numbytes = 3; + } + else + if(s->rows >= 65536) + { + numbytes = 2; + } + else + if(s->rows >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, row); break; + case 1: lt_emit_u16(lt, row); break; + case 2: lt_emit_u24(lt, row); break; + case 3: lt_emit_u32(lt, row); break; + } + } + + rc=lt_emit_string(lt, value); + + if(lt->timebuff) + { + lt->timechangecount++; + if(lt->timecurr) + { + lt->timecurr->next = lt->timebuff; + lt->timecurr = lt->timebuff; + } + else + { + lt->timehead = lt->timecurr = lt->timebuff; + } + + lt->timebuff=NULL; + } + } + +return(rc); +} + + +int lt_emit_value_bit_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value) +{ +int rc=0; +int start_position; +int tag, tagadd; + +if((!lt)||(!s)||(!value)) return(rc); + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if(!(s->flags&(LT_SYM_F_DOUBLE|LT_SYM_F_STRING))) + { + int numbytes; /* number of bytes to store value minus one */ + char *pnt; + int mvl=0; + char ch; + char prevch; + unsigned int last_change_delta; + + if(lt->clock_compress) + if((s->len==1)&&(s->rows==0)) /* possible clock handling */ + { + if(((s->clk_prevval == '1') && (value[0]=='0')) || ((s->clk_prevval == '0') && (value[0]=='1'))) + { + if(s->clk_prevtrans==-1) + { + s->clk_prevtrans = lt->timeval; + s->clk_numtrans = 0; + } + else + if(s->clk_numtrans == 0) + { + s->clk_delta = lt->timeval - s->clk_prevtrans; + s->clk_prevtrans = lt->timeval; + s->clk_numtrans++; + } + else + { + if(s->clk_delta == (lt->timeval - s->clk_prevtrans)) + { + s->clk_numtrans++; + s->clk_prevtrans = lt->timeval; + if(s->clk_numtrans > LT_CLKPACK) + { + s->clk_prevval = value[0]; + + /* printf("Clock value '%c' for '%s' at %d (#%d)\n", value[0], s->name, lt->timeval, s->clk_numtrans); */ + return(1); + } + } + else + { + if(s->clk_numtrans > LT_CLKPACK) + { + lt_flushclock(lt, s); + /* flush clock then continue below! */ + } + else + { + s->clk_prevtrans=-1; + } + } + } + + } + else + { + if(s->clk_numtrans > LT_CLKPACK) + { + lt_flushclock(lt, s); + /* flush clock then continue below! */ + } + else + { + s->clk_prevtrans=-1; + } + } + + s->clk_prevval = value[0]; + } + + /* normal trace handling */ + + last_change_delta = lt->position - s->last_change - 2; + + if(last_change_delta >= 256*65536) + { + numbytes = 3; + } + else + if(last_change_delta >= 65536) + { + numbytes = 2; + } + else + if(last_change_delta >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + pnt = value; + prevch = *pnt; + while((ch=*(pnt++))) + { + switch(ch) + { + case '0': + case '1': mvl|=LT_MVL_2; break; + case 'Z': + case 'z': + case 'X': + case 'x': mvl|=LT_MVL_4; break; + default: mvl|=LT_MVL_9; break; + } + + if(prevch!=ch) prevch = 0; + } + + switch(prevch) + { + case 0x00: tagadd = 0; break; + case '0': tagadd = 3; break; + case '1': tagadd = 4; break; + case 'Z': + case 'z': tagadd = 5; break; + case 'X': + case 'x': tagadd = 6; break; + case 'H': + case 'h': tagadd = 7; break; + case 'U': + case 'u': tagadd = 8; break; + case 'W': + case 'w': tagadd = 9; break; + case 'L': + case 'l': tagadd = 0xa; break; + default: tagadd = 0xb; break; + } + + if(mvl) + { + start_position = lt->position; + if(tagadd) + { + tag = (numbytes<<4) + tagadd; + } + else + { + tag = (numbytes<<4) + ((mvl<_MVL_9)? 2 : ((mvl<_MVL_4)? 1 : 0)); + } + lt_emit_u8(lt, tag); + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, last_change_delta); break; + case 1: lt_emit_u16(lt, last_change_delta); break; + case 2: lt_emit_u24(lt, last_change_delta); break; + case 3: lt_emit_u32(lt, last_change_delta); break; + } + + s->last_change = start_position; + + if(s->rows>0) + { + if(s->rows >= 256*65536) + { + numbytes = 3; + } + else + if(s->rows >= 65536) + { + numbytes = 2; + } + else + if(s->rows >= 256) + { + numbytes = 1; + } + else + { + numbytes = 0; + } + + switch(numbytes&3) + { + case 0: lt_emit_u8(lt, row); break; + case 1: lt_emit_u16(lt, row); break; + case 2: lt_emit_u24(lt, row); break; + case 3: lt_emit_u32(lt, row); break; + } + } + + if(!tagadd) + { + int len = ((s->flags)<_SYM_F_INTEGER) ? 32 : s->len; + if((mvl & (LT_MVL_2|LT_MVL_4|LT_MVL_9)) == LT_MVL_2) + { + int i; + int bitpos = 7; + int outval = 0; + int thisval= 0; + + pnt = value; + + for(i=0;itimebuff) + { + lt->timechangecount++; + if(lt->timecurr) + { + lt->timecurr->next = lt->timebuff; + lt->timecurr = lt->timebuff; + } + else + { + lt->timehead = lt->timecurr = lt->timebuff; + } + + lt->timebuff=NULL; + } + } + +return(rc); +} diff --git a/vpi/lxt_write.h b/vpi/lxt_write.h new file mode 100644 index 000000000..7c95aa045 --- /dev/null +++ b/vpi/lxt_write.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2001 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef DEFS_LXT_H +#define DEFS_LXT_H + +#include +#include +#include +#include +#include + +#define LT_HDRID (0x0138) +#define LT_VERSION (0x0001) +#define LT_TRLID (0xB4) + +#define LT_CLKPACK (4) + +#define LT_MVL_2 (1<<0) +#define LT_MVL_4 (1<<1) +#define LT_MVL_9 (1<<2) + +struct lt_timetrail +{ +struct lt_timetrail *next; +int timeval; +unsigned int position; +}; + + +#define LT_SYMPRIME 65519 + +#define LT_SECTION_END (0) +#define LT_SECTION_CHG (1) +#define LT_SECTION_SYNC_TABLE (2) +#define LT_SECTION_FACNAME (3) +#define LT_SECTION_FACNAME_GEOMETRY (4) +#define LT_SECTION_TIMESCALE (5) +#define LT_SECTION_TIME_TABLE (6) +#define LT_SECTION_INITIAL_VALUE (7) +#define LT_SECTION_DOUBLE_TEST (8) + +struct lt_trace +{ +FILE *handle; +unsigned int position; + +struct lt_symbol *sym[LT_SYMPRIME]; +struct lt_symbol **sorted_facs; +struct lt_symbol *symchain; +int numfacs; +int numfacbytes; +int longestname; +int mintime, maxtime; +int timescale; +int initial_value; + +struct lt_timetrail *timehead, *timecurr, *timebuff; +int timechangecount; +char double_used; +char do_strip_brackets; +char clock_compress; + +unsigned int change_field_offset; +unsigned int facname_offset; +unsigned int facgeometry_offset; +unsigned int time_table_offset; +unsigned int sync_table_offset; +unsigned int initial_value_offset; +unsigned int timescale_offset; +unsigned int double_test_offset; + +char *compress_fac_str; +int compress_fac_len; + +int timeval; /* for clock induction */ +}; + + +struct lt_symbol +{ +struct lt_symbol *next; +struct lt_symbol *symchain; +char *name; +int namlen; + +int facnum; +struct lt_symbol *aliased_to; + +unsigned int rows; +int msb, lsb; +int len; +int flags; + +unsigned int last_change; + +int clk_delta; +int clk_prevtrans; +int clk_numtrans; +char clk_prevval; +}; + +#define LT_SYM_F_BITS (0) +#define LT_SYM_F_INTEGER (1<<0) +#define LT_SYM_F_DOUBLE (1<<1) +#define LT_SYM_F_STRING (1<<2) +#define LT_SYM_F_ALIAS (1<<3) + + +struct lt_trace * lt_init(const char *name); +void lt_close(struct lt_trace *lt); + +struct lt_symbol * lt_symbol_find(struct lt_trace *lt, const char *name); +struct lt_symbol * lt_symbol_add(struct lt_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags); +struct lt_symbol * lt_symbol_alias(struct lt_trace *lt, const char *existing_name, const char *alias, int msb, int lsb); +void lt_symbol_bracket_stripping(struct lt_trace *lt, int doit); + +void lt_set_timescale(struct lt_trace *lt, int timescale); +void lt_set_initial_value(struct lt_trace *lt, char value); +int lt_set_time(struct lt_trace *lt, int timeval); +void lt_set_clock_compress(struct lt_trace *lt); + +/* + * value change functions..note that if the value string len for + * lt_emit_value_bit_string() is shorter than the symbol length + * it will be left justified with the rightmost character used as + * a repeat value that will be propagated to pad the value string out: + * + * "10x" for 8 bits becomes "10xxxxxx" + * "z" for 8 bits becomes "zzzzzzzz" + */ +int lt_emit_value_int(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, int value); +int lt_emit_value_double(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, double value); +int lt_emit_value_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value); +int lt_emit_value_bit_string(struct lt_trace *lt, struct lt_symbol *s, unsigned int row, char *value); + +#endif diff --git a/vpi/sys_lxt.c b/vpi/sys_lxt.c new file mode 100644 index 000000000..5aaa074d9 --- /dev/null +++ b/vpi/sys_lxt.c @@ -0,0 +1,785 @@ +/* + * Copyright (c) 2002 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) && !defined(macintosh) +#ident "$Id: sys_lxt.c,v 1.1 2002/03/09 21:54:49 steve Exp $" +#endif + +# include "config.h" +# include "lxt_write.h" + +/* + * This file contains the implementations of the VCD related + * funcitons. + */ + +# include "vpi_user.h" +# include +# include +# include +# include +# include +#ifdef HAVE_MALLOC_H +# include +#endif + +struct lxt_scope +{ +struct lxt_scope *next, *prev; +char *name; +int len; +}; + +struct lxt_scope *lxt_scope_head=NULL, *lxt_scope_current=NULL; + +static void push_scope(const char *name) +{ +struct lxt_scope *t = (struct lxt_scope *)calloc(1, sizeof(struct lxt_scope)); + +t->name = strdup(name); +t->len = strlen(name); + +if(!lxt_scope_head) + { + lxt_scope_head = lxt_scope_current = t; + } + else + { + lxt_scope_current->next = t; + t->prev = lxt_scope_current; + lxt_scope_current = t; + } +} + +static void pop_scope(void) +{ +struct lxt_scope *t; + +assert(lxt_scope_current); + +t=lxt_scope_current->prev; +free(lxt_scope_current->name); +free(lxt_scope_current); +lxt_scope_current = t; +if(!t) lxt_scope_head = t; +} + +static char *create_full_name(const char *name) +{ +char *n, *n2; +int len = 0; +struct lxt_scope *t = lxt_scope_head; + +while(t) + { + len+=t->len+1; + t=t->next; + } + +len += strlen(name) + 1; +n = n2 = malloc(len); +t = lxt_scope_head; + +while(t) + { + strcpy(n2, t->name); + n2 += t->len; + *n2 = '.'; + n2++; + t=t->next; + } + +strcpy(n2, name); + +return(n); +} + + +static struct lt_trace *dump_file = 0; + +struct vcd_info { + vpiHandle item; + vpiHandle cb; + struct t_vpi_time time; + struct lt_symbol *sym; + struct vcd_info*next; +}; + + +static struct vcd_info*vcd_list = 0; +static unsigned long vcd_cur_time = 0; +static int dump_is_off = 0; + + +static void show_this_item(struct vcd_info*info) +{ + s_vpi_value value; + + value.format = vpiBinStrVal; + vpi_get_value(info->item, &value); + lt_emit_value_bit_string(dump_file, info->sym, 0 /* array row */, value.value.str); +} + + +static void show_this_item_x(struct vcd_info*info) +{ + lt_emit_value_bit_string(dump_file, info->sym, 0 /* array row */, "x"); +} + + +/* + * managed qsorted list of scope names for duplicates bsearching + */ + +struct vcd_names_s { + const char *name; + struct vcd_names_s *next; +}; + +static struct vcd_names_s *vcd_names_list; +static const char **vcd_names_sorted; +static int listed_names, sorted_names; + +inline static void vcd_names_add(const char *name) +{ + struct vcd_names_s *nl = (struct vcd_names_s *) + malloc(sizeof(struct vcd_names_s)); + assert(nl); + nl->name = name; + nl->next = vcd_names_list; + vcd_names_list = nl; + listed_names ++; +} + +static int vcd_names_compare(const void *s1, const void *s2) +{ + const char *v1 = *(const char **) s1; + const char *v2 = *(const char **) s2; + + return strcmp(v1, v2); +} + +static const char *vcd_names_search(const char *key) +{ + const char **v = (const char **) + bsearch(&key, + vcd_names_sorted, sorted_names, + sizeof(const char *), vcd_names_compare ); + + return(v ? *v : NULL); +} + +static void vcd_names_sort(void) +{ + if (listed_names) { + struct vcd_names_s *r; + const char **l; + + sorted_names += listed_names; + vcd_names_sorted = (const char **) + realloc(vcd_names_sorted, + sorted_names*(sizeof(const char *))); + assert(vcd_names_sorted); + + l = vcd_names_sorted + sorted_names - listed_names; + listed_names = 0; + + r = vcd_names_list; + vcd_names_list = 0x0; + + while (r) { + struct vcd_names_s *rr = r; + r = rr->next; + *(l++) = rr->name; + free(rr); + } + + qsort(vcd_names_sorted, sorted_names, + sizeof(const char **), vcd_names_compare); + } +} + + +static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */ +static unsigned long dumpvars_time; +inline static int dump_header_pending(void) +{ + return dumpvars_status != 2; +} + +/* + * This function writes out all the traced variables, whether they + * changed or not. + */ +static void vcd_checkpoint() +{ + struct vcd_info*cur; + + for (cur = vcd_list ; cur ; cur = cur->next) + show_this_item(cur); +} + +static void vcd_checkpoint_x() +{ + struct vcd_info*cur; + + for (cur = vcd_list ; cur ; cur = cur->next) + show_this_item_x(cur); +} + +static int variable_cb(p_cb_data cause) +{ + unsigned long now = cause->time->low; + struct t_cb_data cb; + struct vcd_info*info = (struct vcd_info*)cause->user_data; + + /* Reschedule this event so that it happens for the next + trigger on this variable. */ + cb = *cause; + vpi_register_cb(&cb); + + if (dump_is_off) + return 0; + + if (dump_header_pending()) + return 0; + + if (now != vcd_cur_time) { + lt_set_time(dump_file, now); + vcd_cur_time = now; + } + + show_this_item(info); + + return 0; +} + +static int dumpvars_cb(p_cb_data cause) +{ + if (dumpvars_status != 1) + return 0; + + dumpvars_status = 2; + + dumpvars_time = cause->time->low; + vcd_cur_time = dumpvars_time; + + if (!dump_is_off) { + lt_set_time(dump_file, dumpvars_time); + vcd_checkpoint(); + } + + return 0; +} + +inline static int install_dumpvars_callback(void) +{ + struct t_cb_data cb; + static struct t_vpi_time time; + + if (dumpvars_status == 1) + return 0; + + if (dumpvars_status == 2) { + vpi_mcd_printf(6, "VCD Error:" + " $dumpvars ignored," + " previously called at simtime %lu\n", + dumpvars_time); + return 1; + } + + time.type = vpiSimTime; + cb.time = &time; + cb.reason = cbReadOnlySynch; + cb.cb_rtn = dumpvars_cb; + cb.user_data = 0x0; + cb.obj = 0x0; + + vpi_register_cb(&cb); + + dumpvars_status = 1; + return 0; +} + +static int sys_dumpoff_calltf(char*name) +{ + s_vpi_time now; + + if (dump_is_off) + return 0; + + dump_is_off = 1; + + if (dump_file == 0) + return 0; + + if (dump_header_pending()) + return 0; + + vpi_get_time(0, &now); + if (now.low > vcd_cur_time) + lt_set_time(dump_file, now.low); + vcd_cur_time = now.low; + + vcd_checkpoint_x(); + + return 0; +} + +static int sys_dumpon_calltf(char*name) +{ + s_vpi_time now; + + if (!dump_is_off) + return 0; + + dump_is_off = 0; + + if (dump_file == 0) + return 0; + + if (dump_header_pending()) + return 0; + + vpi_get_time(0, &now); + if (now.low > vcd_cur_time) + lt_set_time(dump_file, now.low); + vcd_cur_time = now.low; + + vcd_checkpoint(); + + return 0; +} + +static int sys_dumpall_calltf(char*name) +{ + s_vpi_time now; + + if (dump_file == 0) + return 0; + + if (dump_header_pending()) + return 0; + + vpi_get_time(0, &now); + if (now.low > vcd_cur_time) + lt_set_time(dump_file, now.low); + vcd_cur_time = now.low; + + vcd_checkpoint(); + + return 0; +} + +static void *close_dumpfile(void) +{ +lt_close(dump_file); +return(dump_file = NULL); +} + +static void open_dumpfile(const char*path) +{ + dump_file = lt_init(path); + + if (dump_file == 0) { + vpi_mcd_printf(6, + "LXT Error: Unable to open %s for output.\n", + path); + return; + } else { + int prec = vpi_get(vpiTimePrecision, 0); + + vpi_mcd_printf(4, + "LXT info: dumpfile %s opened for output.\n", + path); + + assert(prec >= -15); + lt_set_timescale(dump_file, prec); + + lt_set_initial_value(dump_file, 'x'); + lt_set_clock_compress(dump_file); + + atexit((void(*)(void))close_dumpfile); + } +} + +static int sys_dumpfile_calltf(char*name) +{ + char*path; + + vpiHandle sys = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, sys); + vpiHandle item; + + if (argv && (item = vpi_scan(argv))) { + s_vpi_value value; + + if (vpi_get(vpiType, item) != vpiConstant + || vpi_get(vpiConstType, item) != vpiStringConst) { + vpi_mcd_printf(6, + "LXT Error:" + " %s parameter must be a string constant\n", + name); + return 0; + } + + value.format = vpiStringVal; + vpi_get_value(item, &value); + path = strdup(value.value.str); + + vpi_free_object(argv); + + } else { + path = strdup("dumpfile.lxt"); + } + + assert(dump_file == 0); + open_dumpfile(path); + + free(path); + + return 0; +} + +/* + Nexus Id cache + + In structural models, many signals refer to the same nexus. + Some structural models also have very many signals. This cache + saves nexus_id - vcd_id pairs, and reuses the vcd_id when a signal + refers to a nexus that is already dumped. + + The new signal will be listed as a $var, but no callback + will be installed. This saves considerable CPU time and leads + to smalle VCD files. + + The _vpiNexusId is a private (int) property of IVL simulators. +*/ + +struct vcd_id_s +{ + const char *id; + struct vcd_id_s *next; + int nex; +}; + +static inline unsigned ihash(int nex) +{ + unsigned a = nex; + a ^= a>>16; + a ^= a>>8; + return a & 0xff; +} + +static struct vcd_id_s **vcd_ids; + +inline static const char *find_nexus_ident(int nex) +{ + struct vcd_id_s *bucket; + + if (!vcd_ids) { + vcd_ids = (struct vcd_id_s **) + calloc(256, sizeof(struct vcd_id_s*)); + assert(vcd_ids); + } + + bucket = vcd_ids[ihash(nex)]; + while (bucket) { + if (nex == bucket->nex) + return bucket->id; + bucket = bucket->next; + } + + return 0; +} + +inline static void set_nexus_ident(int nex, const char *id) +{ + struct vcd_id_s *bucket; + + assert(vcd_ids); + + bucket = (struct vcd_id_s *) malloc(sizeof(struct vcd_id_s)); + bucket->next = vcd_ids[ihash(nex)]; + bucket->id = id; + bucket->nex = nex; + vcd_ids[ihash(nex)] = bucket; +} + +static void scan_item(unsigned depth, vpiHandle item, int skip) +{ + struct t_cb_data cb; + struct vcd_info* info; + + const char* type; + const char* name; + const char* ident; + int nexus_id; + + switch (vpi_get(vpiType, item)) { + + case vpiNet: type = "wire"; if(0){ + case vpiReg: type = "reg"; } + + if (skip) + break; + + name = vpi_get_str(vpiName, item); + + nexus_id = vpi_get(_vpiNexusId, item); + + if (nexus_id) { + ident = find_nexus_ident(nexus_id); + } else { + ident = 0; + } + + if (!ident) { + ident = create_full_name(name); + + if (nexus_id) + set_nexus_ident(nexus_id, ident); + + info = malloc(sizeof(*info)); + + info->time.type = vpiSimTime; + info->item = item; + info->sym = lt_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiSize, item)-1, 0, LT_SYM_F_BITS); + + cb.time = &info->time; + cb.user_data = (char*)info; + cb.value = NULL; + cb.obj = item; + cb.reason = cbValueChange; + cb.cb_rtn = variable_cb; + + info->next = vcd_list; + vcd_list = info; + + info->cb = vpi_register_cb(&cb); + } + else + { + char *n = create_full_name(name); + lt_symbol_alias(dump_file, ident, n, vpi_get(vpiSize, item)-1, 0); + free(n); + } + + break; + + case vpiModule: type = "module"; if(0){ + case vpiNamedBegin: type = "begin"; }if(0){ + case vpiTask: type = "task"; }if(0){ + case vpiFunction: type = "function"; }if(0){ + case vpiNamedFork: type = "fork"; } + + if (depth > 0) { + int nskip; + vpiHandle argv; + + const char* fullname = + vpi_get_str(vpiFullName, item); + + vpi_mcd_printf(4, + "LXT info:" + " scanning scope %s, %u levels\n", + fullname, depth); + + nskip = sorted_names && fullname + && vcd_names_search(fullname); + + if (!nskip) + vcd_names_add(fullname); + else + vpi_mcd_printf(6, + "LXT warning:" + " ignoring signals" + " in previously scanned scope %s\n", + fullname); + + name = vpi_get_str(vpiName, item); + + push_scope(name); /* keep in type info determination for possible future usage */ + + argv = vpi_iterate(vpiInternalScope, item); + if (argv) { + for (item = vpi_scan(argv) ; + item ; + item = vpi_scan(argv)) { + + scan_item(depth-1, item, nskip); + } + } + + pop_scope(); + } + break; + + default: + vpi_mcd_printf(6, + "LXT Error: $lxtdumpvars: Unsupported parameter " + "type (%d)\n", vpi_get(vpiType, item)); + } +} + +static int draw_scope(vpiHandle item) +{ + int depth; + const char *name; + char *type; + + vpiHandle scope = vpi_handle(vpiScope, item); + if (!scope) + return 0; + + depth = 1 + draw_scope(scope); + name = vpi_get_str(vpiName, scope); + + switch (vpi_get(vpiType, item)) { + case vpiNamedBegin: type = "begin"; break; + case vpiTask: type = "task"; break; + case vpiFunction: type = "function"; break; + case vpiNamedFork: type = "fork"; break; + default: type = "module"; break; + } + + push_scope(name); /* keep in type info determination for possible future usage */ + + return depth; +} + +static int sys_dumpvars_calltf(char*name) +{ + unsigned depth; + s_vpi_value value; + vpiHandle item = 0; + vpiHandle sys = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv; + + if (dump_file == 0) { + open_dumpfile("dumpfile.lxt"); + if (dump_file == 0) + return 0; + } + + if (install_dumpvars_callback()) { + return 0; + } + + argv = vpi_iterate(vpiArgument, sys); + + depth = 0; + if (argv && (item = vpi_scan(argv))) + switch (vpi_get(vpiType, item)) { + case vpiConstant: + case vpiNet: + case vpiReg: + case vpiMemoryWord: + value.format = vpiIntVal; + vpi_get_value(item, &value); + depth = value.value.integer; + break; + } + + if (!depth) + depth = 10000; + + if (!argv) { + // $dumpvars; + // search for the toplevel module + vpiHandle parent = vpi_handle(vpiScope, sys); + while (parent) { + item = parent; + parent = vpi_handle(vpiScope, item); + } + + } else if (!item || !(item = vpi_scan(argv))) { + // $dumpvars(level); + // $dumpvars(); + // dump the current scope + item = vpi_handle(vpiScope, sys); + argv = 0x0; + } + + for ( ; item; item = argv ? vpi_scan(argv) : 0x0) { + + int dep = draw_scope(item); + + vcd_names_sort(); + scan_item(depth, item, 0); + + while (dep--) { + pop_scope(); + } + } + + return 0; +} + +void sys_lxt_register() +{ + s_vpi_systf_data tf_data; + + tf_data.type = vpiSysTask; + tf_data.tfname = "$lxtdumpall"; + tf_data.calltf = sys_dumpall_calltf; + tf_data.compiletf = 0; + tf_data.sizetf = 0; + tf_data.user_data = "$lxtdumpall"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$lxtdumpoff"; + tf_data.calltf = sys_dumpoff_calltf; + tf_data.compiletf = 0; + tf_data.sizetf = 0; + tf_data.user_data = "$lxtdumpoff"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$lxtdumpon"; + tf_data.calltf = sys_dumpon_calltf; + tf_data.compiletf = 0; + tf_data.sizetf = 0; + tf_data.user_data = "$lxtdumpon"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$lxtdumpfile"; + tf_data.calltf = sys_dumpfile_calltf; + tf_data.compiletf = 0; + tf_data.sizetf = 0; + tf_data.user_data = "$lxtdumpfile"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$lxtdumpvars"; + tf_data.calltf = sys_dumpvars_calltf; + tf_data.compiletf = 0; + tf_data.sizetf = 0; + tf_data.user_data = "$lxtdumpvars"; + vpi_register_systf(&tf_data); +} + +/* + * $Log: sys_lxt.c,v $ + * Revision 1.1 2002/03/09 21:54:49 steve + * Add LXT dumper support. (Anthony Bybell) + * + */ + diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 9bcace1b8..c802c8089 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) && !defined(macintosh) -#ident "$Id: sys_table.c,v 1.13 2001/09/30 16:45:10 steve Exp $" +#ident "$Id: sys_table.c,v 1.14 2002/03/09 21:54:49 steve Exp $" #endif # include "config.h" @@ -30,6 +30,7 @@ extern void sys_random_register(); extern void sys_readmem_register(); extern void sys_time_register(); extern void sys_vcd_register(); +extern void sys_lxt_register(); void (*vlog_startup_routines[])() = { sys_finish_register, @@ -39,12 +40,16 @@ void (*vlog_startup_routines[])() = { sys_readmem_register, sys_time_register, sys_vcd_register, + sys_lxt_register, 0 }; /* * $Log: sys_table.c,v $ + * Revision 1.14 2002/03/09 21:54:49 steve + * Add LXT dumper support. (Anthony Bybell) + * * Revision 1.13 2001/09/30 16:45:10 steve * Fix some Cygwin DLL handling. (Venkat Iyer) *