Add initial fst support.
This patch is a slight modification to files Tony Bybell (the author of GTKWave) send to me. We still have a few more changes we plan to make, but this should be functional enough for initial testing. Multi-treading and speed/size flags will be added shortly.
This commit is contained in:
parent
09d59d744d
commit
f11ba81202
|
|
@ -73,6 +73,7 @@ ifeq (@HAVE_LIBBZ2@,yes)
|
||||||
O += sys_lxt.o lxt_write.o
|
O += sys_lxt.o lxt_write.o
|
||||||
endif
|
endif
|
||||||
O += sys_lxt2.o lxt2_write.o
|
O += sys_lxt2.o lxt2_write.o
|
||||||
|
O += sys_fst.o fstapi.o fastlz.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Object files for v2005_math.vpi
|
# Object files for v2005_math.vpi
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,547 @@
|
||||||
|
/*
|
||||||
|
FastLZ - lightning-fast lossless compression library
|
||||||
|
|
||||||
|
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
|
||||||
|
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
|
||||||
|
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
|
||||||
|
|
||||||
|
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 "fastlz.h"
|
||||||
|
|
||||||
|
#if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always check for bound when decompressing.
|
||||||
|
* Generally it is best to leave it defined.
|
||||||
|
*/
|
||||||
|
#define FASTLZ_SAFE
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Give hints to the compiler for branch prediction optimization.
|
||||||
|
*/
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ > 2)
|
||||||
|
#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
|
||||||
|
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
|
||||||
|
#else
|
||||||
|
#define FASTLZ_EXPECT_CONDITIONAL(c) (c)
|
||||||
|
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use inlined functions for supported systems.
|
||||||
|
*/
|
||||||
|
#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C)
|
||||||
|
#define FASTLZ_INLINE inline
|
||||||
|
#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__)
|
||||||
|
#define FASTLZ_INLINE __inline
|
||||||
|
#else
|
||||||
|
#define FASTLZ_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevent accessing more than 8-bit at once, except on x86 architectures.
|
||||||
|
*/
|
||||||
|
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||||
|
#define FASTLZ_STRICT_ALIGN
|
||||||
|
#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */
|
||||||
|
#undef FASTLZ_STRICT_ALIGN
|
||||||
|
#elif defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__amd64) /* GNU C */
|
||||||
|
#undef FASTLZ_STRICT_ALIGN
|
||||||
|
#elif defined(_M_IX86) /* Intel, MSVC */
|
||||||
|
#undef FASTLZ_STRICT_ALIGN
|
||||||
|
#elif defined(__386)
|
||||||
|
#undef FASTLZ_STRICT_ALIGN
|
||||||
|
#elif defined(_X86_) /* MinGW */
|
||||||
|
#undef FASTLZ_STRICT_ALIGN
|
||||||
|
#elif defined(__I86__) /* Digital Mars */
|
||||||
|
#undef FASTLZ_STRICT_ALIGN
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* prototypes */
|
||||||
|
int fastlz_compress(const void* input, int length, void* output);
|
||||||
|
int fastlz_compress_level(int level, const void* input, int length, void* output);
|
||||||
|
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||||
|
|
||||||
|
#define MAX_COPY 32
|
||||||
|
#define MAX_LEN 264 /* 256 + 8 */
|
||||||
|
#define MAX_DISTANCE 8192
|
||||||
|
|
||||||
|
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||||
|
#define FASTLZ_READU16(p) *((const flzuint16*)(p))
|
||||||
|
#else
|
||||||
|
#define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HASH_LOG 13
|
||||||
|
#define HASH_SIZE (1<< HASH_LOG)
|
||||||
|
#define HASH_MASK (HASH_SIZE-1)
|
||||||
|
#define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; }
|
||||||
|
|
||||||
|
#undef FASTLZ_LEVEL
|
||||||
|
#define FASTLZ_LEVEL 1
|
||||||
|
|
||||||
|
#undef FASTLZ_COMPRESSOR
|
||||||
|
#undef FASTLZ_DECOMPRESSOR
|
||||||
|
#define FASTLZ_COMPRESSOR fastlz1_compress
|
||||||
|
#define FASTLZ_DECOMPRESSOR fastlz1_decompress
|
||||||
|
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
|
||||||
|
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
|
||||||
|
#include "fastlz.c"
|
||||||
|
|
||||||
|
#undef FASTLZ_LEVEL
|
||||||
|
#define FASTLZ_LEVEL 2
|
||||||
|
|
||||||
|
#undef MAX_DISTANCE
|
||||||
|
#define MAX_DISTANCE 8191
|
||||||
|
#define MAX_FARDISTANCE (65535+MAX_DISTANCE-1)
|
||||||
|
|
||||||
|
#undef FASTLZ_COMPRESSOR
|
||||||
|
#undef FASTLZ_DECOMPRESSOR
|
||||||
|
#define FASTLZ_COMPRESSOR fastlz2_compress
|
||||||
|
#define FASTLZ_DECOMPRESSOR fastlz2_decompress
|
||||||
|
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output);
|
||||||
|
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout);
|
||||||
|
#include "fastlz.c"
|
||||||
|
|
||||||
|
int fastlz_compress(const void* input, int length, void* output)
|
||||||
|
{
|
||||||
|
/* for short block, choose fastlz1 */
|
||||||
|
if(length < 65536)
|
||||||
|
return fastlz1_compress(input, length, output);
|
||||||
|
|
||||||
|
/* else... */
|
||||||
|
return fastlz2_compress(input, length, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fastlz_decompress(const void* input, int length, void* output, int maxout)
|
||||||
|
{
|
||||||
|
/* magic identifier for compression level */
|
||||||
|
int level = ((*(const flzuint8*)input) >> 5) + 1;
|
||||||
|
|
||||||
|
if(level == 1)
|
||||||
|
return fastlz1_decompress(input, length, output, maxout);
|
||||||
|
if(level == 2)
|
||||||
|
return fastlz2_decompress(input, length, output, maxout);
|
||||||
|
|
||||||
|
/* unknown level, trigger error */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fastlz_compress_level(int level, const void* input, int length, void* output)
|
||||||
|
{
|
||||||
|
if(level == 1)
|
||||||
|
return fastlz1_compress(input, length, output);
|
||||||
|
if(level == 2)
|
||||||
|
return fastlz2_compress(input, length, output);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
||||||
|
|
||||||
|
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output)
|
||||||
|
{
|
||||||
|
const flzuint8* ip = (const flzuint8*) input;
|
||||||
|
const flzuint8* ip_bound = ip + length - 2;
|
||||||
|
const flzuint8* ip_limit = ip + length - 12;
|
||||||
|
flzuint8* op = (flzuint8*) output;
|
||||||
|
|
||||||
|
const flzuint8* htab[HASH_SIZE];
|
||||||
|
const flzuint8** hslot;
|
||||||
|
flzuint32 hval;
|
||||||
|
|
||||||
|
flzuint32 copy;
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4))
|
||||||
|
{
|
||||||
|
if(length)
|
||||||
|
{
|
||||||
|
/* create literal copy only */
|
||||||
|
*op++ = length-1;
|
||||||
|
ip_bound++;
|
||||||
|
while(ip <= ip_bound)
|
||||||
|
*op++ = *ip++;
|
||||||
|
return length+1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initializes hash table */
|
||||||
|
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
|
||||||
|
*hslot = ip;
|
||||||
|
|
||||||
|
/* we start with literal copy */
|
||||||
|
copy = 2;
|
||||||
|
*op++ = MAX_COPY-1;
|
||||||
|
*op++ = *ip++;
|
||||||
|
*op++ = *ip++;
|
||||||
|
|
||||||
|
/* main loop */
|
||||||
|
while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
|
||||||
|
{
|
||||||
|
const flzuint8* ref;
|
||||||
|
flzuint32 distance;
|
||||||
|
|
||||||
|
/* minimum match length */
|
||||||
|
flzuint32 len = 3;
|
||||||
|
|
||||||
|
/* comparison starting-point */
|
||||||
|
const flzuint8* anchor = ip;
|
||||||
|
|
||||||
|
/* check for a run */
|
||||||
|
#if FASTLZ_LEVEL==2
|
||||||
|
if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1))
|
||||||
|
{
|
||||||
|
distance = 1;
|
||||||
|
ip += 3;
|
||||||
|
ref = anchor - 1 + 3;
|
||||||
|
goto match;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* find potential match */
|
||||||
|
HASH_FUNCTION(hval,ip);
|
||||||
|
hslot = htab + hval;
|
||||||
|
ref = htab[hval];
|
||||||
|
|
||||||
|
/* calculate distance to the match */
|
||||||
|
distance = anchor - ref;
|
||||||
|
|
||||||
|
/* update hash table */
|
||||||
|
*hslot = anchor;
|
||||||
|
|
||||||
|
/* is this a match? check the first 3 bytes */
|
||||||
|
if(distance==0 ||
|
||||||
|
#if FASTLZ_LEVEL==1
|
||||||
|
(distance >= MAX_DISTANCE) ||
|
||||||
|
#else
|
||||||
|
(distance >= MAX_FARDISTANCE) ||
|
||||||
|
#endif
|
||||||
|
*ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++)
|
||||||
|
goto literal;
|
||||||
|
|
||||||
|
#if FASTLZ_LEVEL==2
|
||||||
|
/* far, needs at least 5-byte match */
|
||||||
|
if(distance >= MAX_DISTANCE)
|
||||||
|
{
|
||||||
|
if(*ip++ != *ref++ || *ip++!= *ref++)
|
||||||
|
goto literal;
|
||||||
|
len += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
match:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* last matched byte */
|
||||||
|
ip = anchor + len;
|
||||||
|
|
||||||
|
/* distance is biased */
|
||||||
|
distance--;
|
||||||
|
|
||||||
|
if(!distance)
|
||||||
|
{
|
||||||
|
/* zero distance means a run */
|
||||||
|
flzuint8 x = ip[-1];
|
||||||
|
while(ip < ip_bound)
|
||||||
|
if(*ref++ != x) break; else ip++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
/* safe because the outer check against ip limit */
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
while(ip < ip_bound)
|
||||||
|
if(*ref++ != *ip++) break;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we have copied something, adjust the copy count */
|
||||||
|
if(copy)
|
||||||
|
/* copy is biased, '0' means 1 byte copy */
|
||||||
|
*(op-copy-1) = copy-1;
|
||||||
|
else
|
||||||
|
/* back, to overwrite the copy count */
|
||||||
|
op--;
|
||||||
|
|
||||||
|
/* reset literal counter */
|
||||||
|
copy = 0;
|
||||||
|
|
||||||
|
/* length is biased, '1' means a match of 3 bytes */
|
||||||
|
ip -= 3;
|
||||||
|
len = ip - anchor;
|
||||||
|
|
||||||
|
/* encode the match */
|
||||||
|
#if FASTLZ_LEVEL==2
|
||||||
|
if(distance < MAX_DISTANCE)
|
||||||
|
{
|
||||||
|
if(len < 7)
|
||||||
|
{
|
||||||
|
*op++ = (len << 5) + (distance >> 8);
|
||||||
|
*op++ = (distance & 255);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*op++ = (7 << 5) + (distance >> 8);
|
||||||
|
for(len-=7; len >= 255; len-= 255)
|
||||||
|
*op++ = 255;
|
||||||
|
*op++ = len;
|
||||||
|
*op++ = (distance & 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* far away, but not yet in the another galaxy... */
|
||||||
|
if(len < 7)
|
||||||
|
{
|
||||||
|
distance -= MAX_DISTANCE;
|
||||||
|
*op++ = (len << 5) + 31;
|
||||||
|
*op++ = 255;
|
||||||
|
*op++ = distance >> 8;
|
||||||
|
*op++ = distance & 255;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
distance -= MAX_DISTANCE;
|
||||||
|
*op++ = (7 << 5) + 31;
|
||||||
|
for(len-=7; len >= 255; len-= 255)
|
||||||
|
*op++ = 255;
|
||||||
|
*op++ = len;
|
||||||
|
*op++ = 255;
|
||||||
|
*op++ = distance >> 8;
|
||||||
|
*op++ = distance & 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2))
|
||||||
|
while(len > MAX_LEN-2)
|
||||||
|
{
|
||||||
|
*op++ = (7 << 5) + (distance >> 8);
|
||||||
|
*op++ = MAX_LEN - 2 - 7 -2;
|
||||||
|
*op++ = (distance & 255);
|
||||||
|
len -= MAX_LEN-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(len < 7)
|
||||||
|
{
|
||||||
|
*op++ = (len << 5) + (distance >> 8);
|
||||||
|
*op++ = (distance & 255);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*op++ = (7 << 5) + (distance >> 8);
|
||||||
|
*op++ = len - 7;
|
||||||
|
*op++ = (distance & 255);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* update the hash at match boundary */
|
||||||
|
HASH_FUNCTION(hval,ip);
|
||||||
|
htab[hval] = ip++;
|
||||||
|
HASH_FUNCTION(hval,ip);
|
||||||
|
htab[hval] = ip++;
|
||||||
|
|
||||||
|
/* assuming literal copy */
|
||||||
|
*op++ = MAX_COPY-1;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
|
literal:
|
||||||
|
*op++ = *anchor++;
|
||||||
|
ip = anchor;
|
||||||
|
copy++;
|
||||||
|
if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY))
|
||||||
|
{
|
||||||
|
copy = 0;
|
||||||
|
*op++ = MAX_COPY-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* left-over as literal copy */
|
||||||
|
ip_bound++;
|
||||||
|
while(ip <= ip_bound)
|
||||||
|
{
|
||||||
|
*op++ = *ip++;
|
||||||
|
copy++;
|
||||||
|
if(copy == MAX_COPY)
|
||||||
|
{
|
||||||
|
copy = 0;
|
||||||
|
*op++ = MAX_COPY-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we have copied something, adjust the copy length */
|
||||||
|
if(copy)
|
||||||
|
*(op-copy-1) = copy-1;
|
||||||
|
else
|
||||||
|
op--;
|
||||||
|
|
||||||
|
#if FASTLZ_LEVEL==2
|
||||||
|
/* marker for fastlz2 */
|
||||||
|
*(flzuint8*)output |= (1 << 5);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return op - (flzuint8*)output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout)
|
||||||
|
{
|
||||||
|
const flzuint8* ip = (const flzuint8*) input;
|
||||||
|
const flzuint8* ip_limit = ip + length;
|
||||||
|
flzuint8* op = (flzuint8*) output;
|
||||||
|
flzuint8* op_limit = op + maxout;
|
||||||
|
flzuint32 ctrl = (*ip++) & 31;
|
||||||
|
int loop = 1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const flzuint8* ref = op;
|
||||||
|
flzuint32 len = ctrl >> 5;
|
||||||
|
flzuint32 ofs = (ctrl & 31) << 8;
|
||||||
|
|
||||||
|
if(ctrl >= 32)
|
||||||
|
{
|
||||||
|
#if FASTLZ_LEVEL==2
|
||||||
|
flzuint8 code;
|
||||||
|
#endif
|
||||||
|
len--;
|
||||||
|
ref -= ofs;
|
||||||
|
if (len == 7-1)
|
||||||
|
#if FASTLZ_LEVEL==1
|
||||||
|
len += *ip++;
|
||||||
|
ref -= *ip++;
|
||||||
|
#else
|
||||||
|
do
|
||||||
|
{
|
||||||
|
code = *ip++;
|
||||||
|
len += code;
|
||||||
|
} while (code==255);
|
||||||
|
code = *ip++;
|
||||||
|
ref -= code;
|
||||||
|
|
||||||
|
/* match from 16-bit distance */
|
||||||
|
if(FASTLZ_UNEXPECT_CONDITIONAL(code==255))
|
||||||
|
if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8)))
|
||||||
|
{
|
||||||
|
ofs = (*ip++) << 8;
|
||||||
|
ofs += *ip++;
|
||||||
|
ref = op - ofs - MAX_DISTANCE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FASTLZ_SAFE
|
||||||
|
if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output))
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
|
||||||
|
ctrl = *ip++;
|
||||||
|
else
|
||||||
|
loop = 0;
|
||||||
|
|
||||||
|
if(ref == op)
|
||||||
|
{
|
||||||
|
/* optimize copy for a run */
|
||||||
|
flzuint8 b = ref[-1];
|
||||||
|
*op++ = b;
|
||||||
|
*op++ = b;
|
||||||
|
*op++ = b;
|
||||||
|
for(; len; --len)
|
||||||
|
*op++ = b;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||||
|
const flzuint16* p;
|
||||||
|
flzuint16* q;
|
||||||
|
#endif
|
||||||
|
/* copy from reference */
|
||||||
|
ref--;
|
||||||
|
*op++ = *ref++;
|
||||||
|
*op++ = *ref++;
|
||||||
|
*op++ = *ref++;
|
||||||
|
|
||||||
|
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||||
|
/* copy a byte, so that now it's word aligned */
|
||||||
|
if(len & 1)
|
||||||
|
{
|
||||||
|
*op++ = *ref++;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy 16-bit at once */
|
||||||
|
q = (flzuint16*) op;
|
||||||
|
op += len;
|
||||||
|
p = (const flzuint16*) ref;
|
||||||
|
for(len>>=1; len > 4; len-=4)
|
||||||
|
{
|
||||||
|
*q++ = *p++;
|
||||||
|
*q++ = *p++;
|
||||||
|
*q++ = *p++;
|
||||||
|
*q++ = *p++;
|
||||||
|
}
|
||||||
|
for(; len; --len)
|
||||||
|
*q++ = *p++;
|
||||||
|
#else
|
||||||
|
for(; len; --len)
|
||||||
|
*op++ = *ref++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ctrl++;
|
||||||
|
#ifdef FASTLZ_SAFE
|
||||||
|
if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit))
|
||||||
|
return 0;
|
||||||
|
if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit))
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*op++ = *ip++;
|
||||||
|
for(--ctrl; ctrl; ctrl--)
|
||||||
|
*op++ = *ip++;
|
||||||
|
|
||||||
|
loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit);
|
||||||
|
if(loop)
|
||||||
|
ctrl = *ip++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(FASTLZ_EXPECT_CONDITIONAL(loop));
|
||||||
|
|
||||||
|
return op - (flzuint8*)output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
FastLZ - lightning-fast lossless compression library
|
||||||
|
|
||||||
|
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
|
||||||
|
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
|
||||||
|
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
|
||||||
|
|
||||||
|
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 FASTLZ_H
|
||||||
|
#define FASTLZ_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#define flzuint8 uint8_t
|
||||||
|
#define flzuint16 uint16_t
|
||||||
|
#define flzuint32 uint32_t
|
||||||
|
|
||||||
|
|
||||||
|
#define FASTLZ_VERSION 0x000100
|
||||||
|
|
||||||
|
#define FASTLZ_VERSION_MAJOR 0
|
||||||
|
#define FASTLZ_VERSION_MINOR 0
|
||||||
|
#define FASTLZ_VERSION_REVISION 0
|
||||||
|
|
||||||
|
#define FASTLZ_VERSION_STRING "0.1.0"
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
Compress a block of data in the input buffer and returns the size of
|
||||||
|
compressed block. The size of input buffer is specified by length. The
|
||||||
|
minimum input buffer size is 16.
|
||||||
|
|
||||||
|
The output buffer must be at least 5% larger than the input buffer
|
||||||
|
and can not be smaller than 66 bytes.
|
||||||
|
|
||||||
|
If the input is not compressible, the return value might be larger than
|
||||||
|
length (input buffer size).
|
||||||
|
|
||||||
|
The input buffer and the output buffer can not overlap.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int fastlz_compress(const void* input, int length, void* output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Decompress a block of compressed data and returns the size of the
|
||||||
|
decompressed block. If error occurs, e.g. the compressed data is
|
||||||
|
corrupted or the output buffer is not large enough, then 0 (zero)
|
||||||
|
will be returned instead.
|
||||||
|
|
||||||
|
The input buffer and the output buffer can not overlap.
|
||||||
|
|
||||||
|
Decompression is memory safe and guaranteed not to write the output buffer
|
||||||
|
more than what is specified in maxout.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Compress a block of data in the input buffer and returns the size of
|
||||||
|
compressed block. The size of input buffer is specified by length. The
|
||||||
|
minimum input buffer size is 16.
|
||||||
|
|
||||||
|
The output buffer must be at least 5% larger than the input buffer
|
||||||
|
and can not be smaller than 66 bytes.
|
||||||
|
|
||||||
|
If the input is not compressible, the return value might be larger than
|
||||||
|
length (input buffer size).
|
||||||
|
|
||||||
|
The input buffer and the output buffer can not overlap.
|
||||||
|
|
||||||
|
Compression level can be specified in parameter level. At the moment,
|
||||||
|
only level 1 and level 2 are supported.
|
||||||
|
Level 1 is the fastest compression and generally useful for short data.
|
||||||
|
Level 2 is slightly slower but it gives better compression ratio.
|
||||||
|
|
||||||
|
Note that the compressed data, regardless of the level, can always be
|
||||||
|
decompressed using the function fastlz_decompress above.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int fastlz_compress_level(int level, const void* input, int length, void* output);
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* FASTLZ_H */
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2010 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 FST_API_H
|
||||||
|
#define FST_API_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define FST_RDLOAD "FSTLOAD | "
|
||||||
|
|
||||||
|
typedef uint32_t fstHandle;
|
||||||
|
|
||||||
|
enum fstBlockType {
|
||||||
|
FST_BL_HDR = 0,
|
||||||
|
FST_BL_VCDATA = 1,
|
||||||
|
FST_BL_BLACKOUT = 2,
|
||||||
|
FST_BL_GEOM = 3,
|
||||||
|
FST_BL_HIER = 4,
|
||||||
|
|
||||||
|
FST_BL_ZWRAPPER = 254, /* indicates that whole trace is gz wrapped */
|
||||||
|
FST_BL_SKIP = 255 /* used while block is being written */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum fstScopeType {
|
||||||
|
FST_ST_VCD_MIN = 0,
|
||||||
|
FST_ST_VCD_MODULE = 0,
|
||||||
|
FST_ST_VCD_TASK = 1,
|
||||||
|
FST_ST_VCD_FUNCTION = 2,
|
||||||
|
FST_ST_VCD_BEGIN = 3,
|
||||||
|
FST_ST_VCD_FORK = 4,
|
||||||
|
FST_ST_VCD_MAX = 4,
|
||||||
|
|
||||||
|
FST_ST_MAX = 4,
|
||||||
|
|
||||||
|
FST_ST_VCD_SCOPE = 254,
|
||||||
|
FST_ST_VCD_UPSCOPE = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
enum fstVarType {
|
||||||
|
FST_VT_VCD_MIN = 0, /* start of VCD datatypes */
|
||||||
|
FST_VT_VCD_EVENT = 0,
|
||||||
|
FST_VT_VCD_INTEGER = 1,
|
||||||
|
FST_VT_VCD_PARAMETER = 2,
|
||||||
|
FST_VT_VCD_REAL = 3,
|
||||||
|
FST_VT_VCD_REAL_PARAMETER = 4,
|
||||||
|
FST_VT_VCD_REG = 5,
|
||||||
|
FST_VT_VCD_SUPPLY0 = 6,
|
||||||
|
FST_VT_VCD_SUPPLY1 = 7,
|
||||||
|
FST_VT_VCD_TIME = 8,
|
||||||
|
FST_VT_VCD_TRI = 9,
|
||||||
|
FST_VT_VCD_TRIAND = 10,
|
||||||
|
FST_VT_VCD_TRIOR = 11,
|
||||||
|
FST_VT_VCD_TRIREG = 12,
|
||||||
|
FST_VT_VCD_TRI0 = 13,
|
||||||
|
FST_VT_VCD_TRI1 = 14,
|
||||||
|
FST_VT_VCD_WAND = 15,
|
||||||
|
FST_VT_VCD_WIRE = 16,
|
||||||
|
FST_VT_VCD_WOR = 17,
|
||||||
|
FST_VT_VCD_PORT = 18,
|
||||||
|
FST_VT_VCD_ARRAY = 19, /* used to define the rownum (index) port on the array */
|
||||||
|
FST_VT_VCD_REALTIME = 20,
|
||||||
|
FST_VT_VCD_MAX = 20, /* end of VCD datatypes */
|
||||||
|
|
||||||
|
FST_VT_GEN_STRING = 254, /* generic string type (max len is defined as the len in fstWriterCreateVar() */
|
||||||
|
FST_VT_GEN_MEMBLOCK = 255 /* generic memblock type (max len is defined as the len in fstWriterCreateVar() */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum fstVarDir {
|
||||||
|
FST_VD_IMPLICIT = 0,
|
||||||
|
FST_VD_INPUT = 1,
|
||||||
|
FST_VD_OUTPUT = 2,
|
||||||
|
FST_VD_INOUT = 3,
|
||||||
|
|
||||||
|
FST_VD_MAX = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum fstHierType {
|
||||||
|
FST_HT_SCOPE = 0,
|
||||||
|
FST_HT_UPSCOPE = 1,
|
||||||
|
FST_HT_VAR = 2,
|
||||||
|
|
||||||
|
FST_HT_MAX = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fstHier
|
||||||
|
{
|
||||||
|
unsigned char htyp;
|
||||||
|
|
||||||
|
union {
|
||||||
|
/* if htyp == FST_HT_SCOPE */
|
||||||
|
struct fstHierScope {
|
||||||
|
unsigned char typ; /* FST_ST_VCD_MODULE ... FST_ST_VCD_FORK */
|
||||||
|
const char *name;
|
||||||
|
const char *component;
|
||||||
|
} scope;
|
||||||
|
|
||||||
|
/* if htyp == FST_HT_VAR */
|
||||||
|
struct fstHierVar {
|
||||||
|
unsigned char typ; /* FST_VT_VCD_EVENT ... FST_VT_VCD_WOR */
|
||||||
|
unsigned char direction; /* FST_VD_IMPLICIT ... FST_VD_INOUT */
|
||||||
|
const char *name;
|
||||||
|
uint32_t length;
|
||||||
|
fstHandle handle;
|
||||||
|
unsigned is_alias : 1;
|
||||||
|
} var;
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* writer functions
|
||||||
|
*/
|
||||||
|
fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd,
|
||||||
|
uint32_t len, const char *nam, fstHandle aliasHandle);
|
||||||
|
|
||||||
|
void fstWriterSetPackType(void *ctx, int typ); /* type = 0 (libz), 1 (fastlz) */
|
||||||
|
void fstWriterSetRepackOnClose(void *ctx, int enable); /* type = 0 (none), 1 (libz) */
|
||||||
|
void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes);
|
||||||
|
int fstWriterGetDumpSizeLimitReached(void *ctx);
|
||||||
|
|
||||||
|
void *fstWriterCreate(const char *nam, int use_compressed_hier);
|
||||||
|
void fstWriterClose(void *ctx);
|
||||||
|
void fstWriterSetDate(void *ctx, const char *dat);
|
||||||
|
void fstWriterSetVersion(void *ctx, const char *vers);
|
||||||
|
void fstWriterSetTimescale(void *ctx, int ts);
|
||||||
|
void fstWriterSetTimescaleFromString(void *ctx, const char *s);
|
||||||
|
void fstWriterSetScope(void *ctx, enum fstScopeType scopetype,
|
||||||
|
const char *scopename, const char *scopecomp);
|
||||||
|
void fstWriterSetUpscope(void *ctx);
|
||||||
|
void fstWriterEmitValueChange(void *ctx, fstHandle handle, void *val);
|
||||||
|
void fstWriterEmitDumpActive(void *ctx, int enable);
|
||||||
|
void fstWriterEmitTimeChange(void *ctx, uint64_t tim);
|
||||||
|
void fstWriterFlushContext(void *ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* reader functions
|
||||||
|
*/
|
||||||
|
void *fstReaderOpen(const char *nam);
|
||||||
|
void fstReaderClose(void *ctx);
|
||||||
|
|
||||||
|
void fstReaderProcessHier(void *ctx, FILE *vcdhandle);
|
||||||
|
void fstReaderIterateHierRewind(void *ctx);
|
||||||
|
struct fstHier *fstReaderIterateHier(void *ctx);
|
||||||
|
|
||||||
|
void fstReaderResetScope(void *ctx);
|
||||||
|
const char *fstReaderPopScope(void *ctx);
|
||||||
|
const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info);
|
||||||
|
const char *fstReaderGetCurrentFlatScope(void *ctx);
|
||||||
|
void *fstReaderGetCurrentScopeUserInfo(void *ctx);
|
||||||
|
|
||||||
|
signed char fstReaderGetTimescale(void *ctx);
|
||||||
|
uint64_t fstReaderGetStartTime(void *ctx);
|
||||||
|
uint64_t fstReaderGetEndTime(void *ctx);
|
||||||
|
uint64_t fstReaderGetMemoryUsedByWriter(void *ctx);
|
||||||
|
uint64_t fstReaderGetScopeCount(void *ctx);
|
||||||
|
uint64_t fstReaderGetVarCount(void *ctx);
|
||||||
|
fstHandle fstReaderGetMaxHandle(void *ctx);
|
||||||
|
uint64_t fstReaderGetAliasCount(void *ctx);
|
||||||
|
uint64_t fstReaderGetValueChangeSectionCount(void *ctx);
|
||||||
|
int fstReaderGetDoubleEndianMatchState(void *ctx);
|
||||||
|
const char *fstReaderGetVersionString(void *ctx);
|
||||||
|
const char *fstReaderGetDateString(void *ctx);
|
||||||
|
|
||||||
|
void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time);
|
||||||
|
void fstReaderSetUnlimitedTimeRange(void *ctx);
|
||||||
|
|
||||||
|
uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx);
|
||||||
|
uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx);
|
||||||
|
unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx);
|
||||||
|
|
||||||
|
int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx);
|
||||||
|
void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx);
|
||||||
|
void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx);
|
||||||
|
void fstReaderSetFacProcessMaskAll(void *ctx);
|
||||||
|
void fstReaderClrFacProcessMaskAll(void *ctx);
|
||||||
|
|
||||||
|
void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable);
|
||||||
|
|
||||||
|
int fstReaderIterBlocks(void *ctx,
|
||||||
|
void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, const unsigned char *value),
|
||||||
|
void *user_callback_data_pointer, FILE *vcdhandle);
|
||||||
|
|
||||||
|
char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,868 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1999-2010 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
# include "sys_priv.h"
|
||||||
|
# include "vcd_priv.h"
|
||||||
|
# include "fstapi.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file contains the implementations of the VCD related
|
||||||
|
* funcitons.
|
||||||
|
*/
|
||||||
|
|
||||||
|
# include <stdio.h>
|
||||||
|
# include <stdlib.h>
|
||||||
|
# include <string.h>
|
||||||
|
# include <assert.h>
|
||||||
|
# include <time.h>
|
||||||
|
#ifdef HAVE_MALLOC_H
|
||||||
|
# include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static char *dump_path = NULL;
|
||||||
|
static struct fstContext *dump_file = NULL;
|
||||||
|
|
||||||
|
struct vcd_info {
|
||||||
|
vpiHandle item;
|
||||||
|
vpiHandle cb;
|
||||||
|
struct t_vpi_time time;
|
||||||
|
struct vcd_info *next;
|
||||||
|
struct vcd_info *dmp_next;
|
||||||
|
fstHandle handle;
|
||||||
|
int scheduled;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static struct vcd_info *vcd_list = NULL;
|
||||||
|
static struct vcd_info *vcd_dmp_list = NULL;
|
||||||
|
static PLI_UINT64 vcd_cur_time = 0;
|
||||||
|
static int dump_is_off = 0;
|
||||||
|
static long dump_limit = 0;
|
||||||
|
static int dump_is_full = 0;
|
||||||
|
static int finish_status = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static const char*units_names[] = {
|
||||||
|
"s",
|
||||||
|
"ms",
|
||||||
|
"us",
|
||||||
|
"ns",
|
||||||
|
"ps",
|
||||||
|
"fs"
|
||||||
|
};
|
||||||
|
|
||||||
|
static void show_this_item(struct vcd_info*info)
|
||||||
|
{
|
||||||
|
s_vpi_value value;
|
||||||
|
PLI_INT32 type = vpi_get(vpiType, info->item);
|
||||||
|
|
||||||
|
if (type == vpiRealVar) {
|
||||||
|
value.format = vpiRealVal;
|
||||||
|
vpi_get_value(info->item, &value);
|
||||||
|
fstWriterEmitValueChange(dump_file, info->handle, &value.value.real);
|
||||||
|
} else {
|
||||||
|
value.format = vpiBinStrVal;
|
||||||
|
vpi_get_value(info->item, &value);
|
||||||
|
fstWriterEmitValueChange(dump_file, info->handle, value.value.str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump values for a $dumpoff. */
|
||||||
|
static void show_this_item_x(struct vcd_info*info)
|
||||||
|
{
|
||||||
|
PLI_INT32 type = vpi_get(vpiType, info->item);
|
||||||
|
|
||||||
|
if (type == vpiRealVar) {
|
||||||
|
/* Some tools dump nothing here...? */
|
||||||
|
double mynan = strtod("NaN", NULL);
|
||||||
|
fstWriterEmitValueChange(dump_file, info->handle, &mynan);
|
||||||
|
} else if (type == vpiNamedEvent) {
|
||||||
|
/* Do nothing for named events. */
|
||||||
|
} else {
|
||||||
|
int siz = vpi_get(vpiSize, info->item);
|
||||||
|
char *xmem = malloc(siz);
|
||||||
|
memset(xmem, 'x', siz);
|
||||||
|
fstWriterEmitValueChange(dump_file, info->handle, xmem);
|
||||||
|
free(xmem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* managed qsorted list of scope names/variables for duplicates bsearching
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct vcd_names_list_s fst_tab = { 0 };
|
||||||
|
struct vcd_names_list_s fst_var = { 0 };
|
||||||
|
|
||||||
|
|
||||||
|
static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */
|
||||||
|
static PLI_UINT64 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 PLI_INT32 variable_cb_2(p_cb_data cause)
|
||||||
|
{
|
||||||
|
struct vcd_info* info = vcd_dmp_list;
|
||||||
|
PLI_UINT64 now = timerec_to_time64(cause->time);
|
||||||
|
|
||||||
|
if (now != vcd_cur_time) {
|
||||||
|
fstWriterEmitTimeChange(dump_file, now);
|
||||||
|
vcd_cur_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
show_this_item(info);
|
||||||
|
info->scheduled = 0;
|
||||||
|
} while ((info = info->dmp_next) != 0);
|
||||||
|
|
||||||
|
vcd_dmp_list = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 variable_cb_1(p_cb_data cause)
|
||||||
|
{
|
||||||
|
struct t_cb_data cb;
|
||||||
|
struct vcd_info*info = (struct vcd_info*)cause->user_data;
|
||||||
|
|
||||||
|
if (dump_is_full) return 0;
|
||||||
|
if (dump_is_off) return 0;
|
||||||
|
if (dump_header_pending()) return 0;
|
||||||
|
if (info->scheduled) return 0;
|
||||||
|
|
||||||
|
if ((dump_limit > 0) && fstWriterGetDumpSizeLimitReached(dump_file)) {
|
||||||
|
dump_is_full = 1;
|
||||||
|
vpi_printf("WARNING: Dump file limit (%ld bytes) "
|
||||||
|
"exceeded.\n", dump_limit);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vcd_dmp_list) {
|
||||||
|
cb = *cause;
|
||||||
|
cb.reason = cbReadOnlySynch;
|
||||||
|
cb.cb_rtn = variable_cb_2;
|
||||||
|
vpi_register_cb(&cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
info->scheduled = 1;
|
||||||
|
info->dmp_next = vcd_dmp_list;
|
||||||
|
vcd_dmp_list = info;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 dumpvars_cb(p_cb_data cause)
|
||||||
|
{
|
||||||
|
if (dumpvars_status != 1) return 0;
|
||||||
|
|
||||||
|
dumpvars_status = 2;
|
||||||
|
|
||||||
|
dumpvars_time = timerec_to_time64(cause->time);
|
||||||
|
vcd_cur_time = dumpvars_time;
|
||||||
|
|
||||||
|
/* nothing to do for $enddefinitions $end */
|
||||||
|
|
||||||
|
if (!dump_is_off) {
|
||||||
|
fstWriterEmitTimeChange(dump_file, dumpvars_time);
|
||||||
|
/* nothing to do for $dumpvars... */
|
||||||
|
vcd_checkpoint();
|
||||||
|
/* ...nothing to do for $end */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 finish_cb(p_cb_data cause)
|
||||||
|
{
|
||||||
|
struct vcd_info *cur, *next;
|
||||||
|
|
||||||
|
if (finish_status != 0) return 0;
|
||||||
|
|
||||||
|
finish_status = 1;
|
||||||
|
|
||||||
|
dumpvars_time = timerec_to_time64(cause->time);
|
||||||
|
|
||||||
|
if (!dump_is_off && !dump_is_full && dumpvars_time != vcd_cur_time) {
|
||||||
|
fstWriterEmitTimeChange(dump_file, dumpvars_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
fstWriterClose(dump_file);
|
||||||
|
|
||||||
|
for (cur = vcd_list ; cur ; cur = next) {
|
||||||
|
next = cur->next;
|
||||||
|
free(cur);
|
||||||
|
}
|
||||||
|
vcd_list = 0;
|
||||||
|
vcd_names_delete(&fst_tab);
|
||||||
|
vcd_names_delete(&fst_var);
|
||||||
|
nexus_ident_delete();
|
||||||
|
free(dump_path);
|
||||||
|
dump_path = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__inline__ static int install_dumpvars_callback(void)
|
||||||
|
{
|
||||||
|
struct t_cb_data cb;
|
||||||
|
static struct t_vpi_time now;
|
||||||
|
|
||||||
|
if (dumpvars_status == 1) return 0;
|
||||||
|
|
||||||
|
if (dumpvars_status == 2) {
|
||||||
|
vpi_printf("FST warning: $dumpvars ignored, previously"
|
||||||
|
" called at simtime %" PLI_UINT64_FMT "\n",
|
||||||
|
dumpvars_time);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
now.type = vpiSimTime;
|
||||||
|
cb.time = &now;
|
||||||
|
cb.reason = cbReadOnlySynch;
|
||||||
|
cb.cb_rtn = dumpvars_cb;
|
||||||
|
cb.user_data = 0x0;
|
||||||
|
cb.obj = 0x0;
|
||||||
|
|
||||||
|
vpi_register_cb(&cb);
|
||||||
|
|
||||||
|
cb.reason = cbEndOfSimulation;
|
||||||
|
cb.cb_rtn = finish_cb;
|
||||||
|
|
||||||
|
vpi_register_cb(&cb);
|
||||||
|
|
||||||
|
dumpvars_status = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_dumpoff_calltf(PLI_BYTE8*name)
|
||||||
|
{
|
||||||
|
s_vpi_time now;
|
||||||
|
PLI_UINT64 now64;
|
||||||
|
|
||||||
|
if (dump_is_off) return 0;
|
||||||
|
|
||||||
|
dump_is_off = 1;
|
||||||
|
|
||||||
|
if (dump_file == 0) return 0;
|
||||||
|
if (dump_header_pending()) return 0;
|
||||||
|
|
||||||
|
now.type = vpiSimTime;
|
||||||
|
vpi_get_time(0, &now);
|
||||||
|
now64 = timerec_to_time64(&now);
|
||||||
|
|
||||||
|
if (now64 > vcd_cur_time) {
|
||||||
|
fstWriterEmitTimeChange(dump_file, now64);
|
||||||
|
vcd_cur_time = now64;
|
||||||
|
}
|
||||||
|
|
||||||
|
fstWriterEmitDumpActive(dump_file, 0); /* $dumpoff */
|
||||||
|
vcd_checkpoint_x();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_dumpon_calltf(PLI_BYTE8*name)
|
||||||
|
{
|
||||||
|
s_vpi_time now;
|
||||||
|
PLI_UINT64 now64;
|
||||||
|
|
||||||
|
if (!dump_is_off) return 0;
|
||||||
|
|
||||||
|
dump_is_off = 0;
|
||||||
|
|
||||||
|
if (dump_file == 0) return 0;
|
||||||
|
if (dump_header_pending()) return 0;
|
||||||
|
|
||||||
|
now.type = vpiSimTime;
|
||||||
|
vpi_get_time(0, &now);
|
||||||
|
now64 = timerec_to_time64(&now);
|
||||||
|
|
||||||
|
if (now64 > vcd_cur_time) {
|
||||||
|
fstWriterEmitTimeChange(dump_file, now64);
|
||||||
|
vcd_cur_time = now64;
|
||||||
|
}
|
||||||
|
|
||||||
|
fstWriterEmitDumpActive(dump_file, 1); /* $dumpon */
|
||||||
|
vcd_checkpoint();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_dumpall_calltf(PLI_BYTE8*name)
|
||||||
|
{
|
||||||
|
s_vpi_time now;
|
||||||
|
PLI_UINT64 now64;
|
||||||
|
|
||||||
|
if (dump_is_off) return 0;
|
||||||
|
if (dump_file == 0) return 0;
|
||||||
|
if (dump_header_pending()) return 0;
|
||||||
|
|
||||||
|
now.type = vpiSimTime;
|
||||||
|
vpi_get_time(0, &now);
|
||||||
|
now64 = timerec_to_time64(&now);
|
||||||
|
|
||||||
|
if (now64 > vcd_cur_time) {
|
||||||
|
fstWriterEmitTimeChange(dump_file, now64);
|
||||||
|
vcd_cur_time = now64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nothing to do for $dumpall... */
|
||||||
|
vcd_checkpoint();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void open_dumpfile(vpiHandle callh)
|
||||||
|
{
|
||||||
|
if (dump_path == 0) dump_path = strdup("dump.fst");
|
||||||
|
|
||||||
|
dump_file = fstWriterCreate(dump_path, 1);
|
||||||
|
|
||||||
|
if (dump_file == 0) {
|
||||||
|
vpi_printf("FST Error: %s:%d: ", vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh));
|
||||||
|
vpi_printf("Unable to open %s for output.\n", dump_path);
|
||||||
|
vpi_control(vpiFinish, 1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
int prec = vpi_get(vpiTimePrecision, 0);
|
||||||
|
unsigned scale = 1;
|
||||||
|
unsigned udx = 0;
|
||||||
|
time_t walltime;
|
||||||
|
char scale_buf[65];
|
||||||
|
|
||||||
|
vpi_printf("FST info: dumpfile %s opened for output.\n",
|
||||||
|
dump_path);
|
||||||
|
|
||||||
|
time(&walltime);
|
||||||
|
|
||||||
|
assert(prec >= -15);
|
||||||
|
while (prec < 0) {
|
||||||
|
udx += 1;
|
||||||
|
prec += 3;
|
||||||
|
}
|
||||||
|
while (prec > 0) {
|
||||||
|
scale *= 10;
|
||||||
|
prec -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fstWriterSetDate(dump_file, asctime(localtime(&walltime)));
|
||||||
|
fstWriterSetVersion(dump_file, "Icarus Verilog");
|
||||||
|
sprintf(scale_buf, "\t%u%s\n", scale, units_names[udx]);
|
||||||
|
fstWriterSetTimescaleFromString(dump_file, scale_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
|
||||||
|
{
|
||||||
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||||
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
/* $dumpfile must be called before $dumpvars starts! */
|
||||||
|
if (dumpvars_status != 0) {
|
||||||
|
char msg [64];
|
||||||
|
snprintf(msg, 64, "FST warning: %s:%d:",
|
||||||
|
vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh));
|
||||||
|
vpi_printf("%s %s called after $dumpvars started,\n", msg, name);
|
||||||
|
vpi_printf("%*s using existing file (%s).\n",
|
||||||
|
(int) strlen(msg), " ", dump_path);
|
||||||
|
vpi_free_object(argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = get_filename(callh, name, vpi_scan(argv));
|
||||||
|
vpi_free_object(argv);
|
||||||
|
if (! path) return 0;
|
||||||
|
|
||||||
|
if (dump_path) {
|
||||||
|
vpi_printf("FST warning: %s:%d: ", vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh));
|
||||||
|
vpi_printf("Overriding dump file %s with %s.\n", dump_path, path);
|
||||||
|
free(dump_path);
|
||||||
|
}
|
||||||
|
dump_path = path;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_dumpflush_calltf(PLI_BYTE8*name)
|
||||||
|
{
|
||||||
|
if (dump_file) fstWriterFlushContext(dump_file);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_dumplimit_calltf(PLI_BYTE8 *name)
|
||||||
|
{
|
||||||
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||||
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||||
|
s_vpi_value val;
|
||||||
|
|
||||||
|
/* Get the value and set the dump limit. */
|
||||||
|
val.format = vpiIntVal;
|
||||||
|
vpi_get_value(vpi_scan(argv), &val);
|
||||||
|
dump_limit = val.value.integer;
|
||||||
|
fstWriterSetDumpSizeLimit(dump_file, dump_limit);
|
||||||
|
|
||||||
|
vpi_free_object(argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scan_item(unsigned depth, vpiHandle item, int skip)
|
||||||
|
{
|
||||||
|
struct t_cb_data cb;
|
||||||
|
struct vcd_info* info;
|
||||||
|
|
||||||
|
PLI_INT32 type;
|
||||||
|
const char* name;
|
||||||
|
const char* fullname;
|
||||||
|
const char* prefix;
|
||||||
|
const char *ident;
|
||||||
|
fstHandle new_ident;
|
||||||
|
int nexus_id;
|
||||||
|
unsigned size;
|
||||||
|
PLI_INT32 item_type;
|
||||||
|
|
||||||
|
/* list of types to iterate upon */
|
||||||
|
int i;
|
||||||
|
static int types[] = {
|
||||||
|
/* Value */
|
||||||
|
vpiNamedEvent,
|
||||||
|
vpiNet,
|
||||||
|
// vpiParameter,
|
||||||
|
vpiReg,
|
||||||
|
vpiVariables,
|
||||||
|
/* Scope */
|
||||||
|
vpiFunction,
|
||||||
|
vpiModule,
|
||||||
|
vpiNamedBegin,
|
||||||
|
vpiNamedFork,
|
||||||
|
vpiTask,
|
||||||
|
-1
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get the displayed type for the various $var and $scope types. */
|
||||||
|
/* Not all of these are supported now, but they should be in a
|
||||||
|
* future development version. */
|
||||||
|
item_type = vpi_get(vpiType, item);
|
||||||
|
switch (item_type) {
|
||||||
|
case vpiNamedEvent: type = FST_VT_VCD_EVENT; break;
|
||||||
|
case vpiIntegerVar: type = FST_VT_VCD_INTEGER; break;
|
||||||
|
case vpiParameter: type = FST_VT_VCD_PARAMETER; break;
|
||||||
|
/* Icarus converts realtime to real. */
|
||||||
|
case vpiRealVar: type = FST_VT_VCD_REAL; break;
|
||||||
|
case vpiMemoryWord:
|
||||||
|
case vpiReg: type = FST_VT_VCD_REG; break;
|
||||||
|
/* Icarus converts a time to a plain register. */
|
||||||
|
case vpiTimeVar: type = FST_VT_VCD_TIME; break;
|
||||||
|
case vpiNet:
|
||||||
|
switch (vpi_get(vpiNetType, item)) {
|
||||||
|
case vpiWand: type = FST_VT_VCD_WAND; break;
|
||||||
|
case vpiWor: type = FST_VT_VCD_WOR; break;
|
||||||
|
case vpiTri: type = FST_VT_VCD_TRI; break;
|
||||||
|
case vpiTri0: type = FST_VT_VCD_TRI0; break;
|
||||||
|
case vpiTri1: type = FST_VT_VCD_TRI1; break;
|
||||||
|
case vpiTriReg: type = FST_VT_VCD_TRIREG; break;
|
||||||
|
case vpiTriAnd: type = FST_VT_VCD_TRIAND; break;
|
||||||
|
case vpiTriOr: type = FST_VT_VCD_TRIOR; break;
|
||||||
|
case vpiSupply1: type = FST_VT_VCD_SUPPLY1; break;
|
||||||
|
case vpiSupply0: type = FST_VT_VCD_SUPPLY0; break;
|
||||||
|
default: type = FST_VT_VCD_WIRE; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case vpiNamedBegin: type = FST_ST_VCD_BEGIN; break;
|
||||||
|
case vpiNamedFork: type = FST_ST_VCD_FORK; break;
|
||||||
|
case vpiFunction: type = FST_ST_VCD_FUNCTION; break;
|
||||||
|
case vpiModule: type = FST_ST_VCD_MODULE; break;
|
||||||
|
case vpiTask: type = FST_ST_VCD_TASK; break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
vpi_printf("FST warning: $dumpvars: Unsupported argument "
|
||||||
|
"type (%s)\n", vpi_get_str(vpiType, item));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do some special processing/checking on array words. Dumping
|
||||||
|
* array words is an Icarus extension. */
|
||||||
|
if (item_type == vpiMemoryWord) {
|
||||||
|
/* Turn a non-constant array word select into a constant
|
||||||
|
* word select. */
|
||||||
|
if (vpi_get(vpiConstantSelect, item) == 0) {
|
||||||
|
vpiHandle array = vpi_handle(vpiParent, item);
|
||||||
|
PLI_INT32 idx = vpi_get(vpiIndex, item);
|
||||||
|
item = vpi_handle_by_index(array, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An array word is implicitly escaped so look for an
|
||||||
|
* escaped identifier that this could conflict with. */
|
||||||
|
/* This does not work as expected since we always find at
|
||||||
|
* least the array word. We likely need a custom routine. */
|
||||||
|
if (vpi_get(vpiType, item) == vpiMemoryWord &&
|
||||||
|
vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) {
|
||||||
|
vpi_printf("FST warning: array word %s will conflict "
|
||||||
|
"with an escaped identifier.\n",
|
||||||
|
vpi_get_str(vpiFullName, item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fullname = vpi_get_str(vpiFullName, item);
|
||||||
|
|
||||||
|
/* Generate the $var or $scope commands. */
|
||||||
|
switch (item_type) {
|
||||||
|
case vpiParameter:
|
||||||
|
vpi_printf("VCD sorry: $dumpvars: can not dump parameters.\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case vpiNamedEvent:
|
||||||
|
case vpiIntegerVar:
|
||||||
|
// case vpiParameter:
|
||||||
|
case vpiRealVar:
|
||||||
|
case vpiMemoryWord:
|
||||||
|
case vpiReg:
|
||||||
|
case vpiTimeVar:
|
||||||
|
case vpiNet:
|
||||||
|
|
||||||
|
/* If we are skipping all signal or this is in an automatic
|
||||||
|
* scope then just return. */
|
||||||
|
if (skip || vpi_get(vpiAutomatic, item)) return;
|
||||||
|
|
||||||
|
/* Skip this signal if it has already been included.
|
||||||
|
* This can only happen for implicitly given signals. */
|
||||||
|
if (vcd_names_search(&fst_var, fullname)) return;
|
||||||
|
|
||||||
|
/* Declare the variable in the VCD file. */
|
||||||
|
name = vpi_get_str(vpiName, item);
|
||||||
|
prefix = is_escaped_id(name) ? "\\" : "";
|
||||||
|
|
||||||
|
/* Some signals can have an alias so handle that. */
|
||||||
|
nexus_id = vpi_get(_vpiNexusId, item);
|
||||||
|
|
||||||
|
ident = 0;
|
||||||
|
if (nexus_id) ident = find_nexus_ident(nexus_id);
|
||||||
|
|
||||||
|
/* Named events do not have a size, but other tools use
|
||||||
|
* a size of 1 and some viewers do not accept a width of
|
||||||
|
* zero so we will also use a width of one for events. */
|
||||||
|
if (item_type == vpiNamedEvent) size = 1;
|
||||||
|
else size = vpi_get(vpiSize, item);
|
||||||
|
|
||||||
|
|
||||||
|
if (vpi_get(vpiSize, item) > 1
|
||||||
|
|| vpi_get(vpiLeftRange, item) != 0)
|
||||||
|
{
|
||||||
|
int slen = strlen(name);
|
||||||
|
char *buf = malloc(slen + 65);
|
||||||
|
sprintf(buf, "%s [%i:%i]", name,
|
||||||
|
(int)vpi_get(vpiLeftRange, item),
|
||||||
|
(int)vpi_get(vpiRightRange, item));
|
||||||
|
|
||||||
|
|
||||||
|
new_ident = fstWriterCreateVar(dump_file, type, FST_VD_IMPLICIT, size, buf, (fstHandle)ident);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_ident = fstWriterCreateVar(dump_file, type, FST_VD_IMPLICIT, size, name, (fstHandle)ident);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ident) {
|
||||||
|
if (nexus_id) set_nexus_ident(nexus_id, (const char *)new_ident);
|
||||||
|
|
||||||
|
/* Add a callback for the signal. */
|
||||||
|
info = malloc(sizeof(*info));
|
||||||
|
|
||||||
|
info->time.type = vpiSimTime;
|
||||||
|
info->item = item;
|
||||||
|
info->handle = new_ident;
|
||||||
|
info->scheduled = 0;
|
||||||
|
|
||||||
|
cb.time = &info->time;
|
||||||
|
cb.user_data = (char*)info;
|
||||||
|
cb.value = NULL;
|
||||||
|
cb.obj = item;
|
||||||
|
cb.reason = cbValueChange;
|
||||||
|
cb.cb_rtn = variable_cb_1;
|
||||||
|
|
||||||
|
info->dmp_next = 0;
|
||||||
|
info->next = vcd_list;
|
||||||
|
vcd_list = info;
|
||||||
|
|
||||||
|
info->cb = vpi_register_cb(&cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case vpiModule:
|
||||||
|
case vpiNamedBegin:
|
||||||
|
case vpiTask:
|
||||||
|
case vpiFunction:
|
||||||
|
case vpiNamedFork:
|
||||||
|
|
||||||
|
if (depth > 0) {
|
||||||
|
int nskip = (vcd_names_search(&fst_tab, fullname) != 0);
|
||||||
|
|
||||||
|
/* We have to always scan the scope because the
|
||||||
|
* depth could be different for this call. */
|
||||||
|
if (nskip) {
|
||||||
|
vpi_printf("FST warning: ignoring signals in "
|
||||||
|
"previously scanned scope %s.\n", fullname);
|
||||||
|
} else {
|
||||||
|
vcd_names_add(&fst_tab, fullname);
|
||||||
|
vcd_names_sort(&fst_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
name = vpi_get_str(vpiName, item);
|
||||||
|
fstWriterSetScope(dump_file, type, name, NULL);
|
||||||
|
|
||||||
|
for (i=0; types[i]>0; i++) {
|
||||||
|
vpiHandle hand;
|
||||||
|
vpiHandle argv = vpi_iterate(types[i], item);
|
||||||
|
while (argv && (hand = vpi_scan(argv))) {
|
||||||
|
scan_item(depth-1, hand, nskip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sort any signals that we added above. */
|
||||||
|
fstWriterSetUpscope(dump_file);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int draw_scope(vpiHandle item, vpiHandle callh)
|
||||||
|
{
|
||||||
|
int depth;
|
||||||
|
const char *name;
|
||||||
|
PLI_INT32 type;
|
||||||
|
|
||||||
|
vpiHandle scope = vpi_handle(vpiScope, item);
|
||||||
|
if (!scope) return 0;
|
||||||
|
|
||||||
|
depth = 1 + draw_scope(scope, callh);
|
||||||
|
name = vpi_get_str(vpiName, scope);
|
||||||
|
|
||||||
|
switch (vpi_get(vpiType, scope)) {
|
||||||
|
case vpiNamedBegin: type = FST_ST_VCD_BEGIN; break;
|
||||||
|
case vpiTask: type = FST_ST_VCD_TASK; break;
|
||||||
|
case vpiFunction: type = FST_ST_VCD_FUNCTION; break;
|
||||||
|
case vpiNamedFork: type = FST_ST_VCD_FORK; break;
|
||||||
|
case vpiModule: type = FST_ST_VCD_MODULE; break;
|
||||||
|
default:
|
||||||
|
type = FST_ST_VCD_MODULE;
|
||||||
|
vpi_printf("FST Error: %s:%d: $dumpvars: Unsupported scope "
|
||||||
|
"type (%d)\n", vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh),
|
||||||
|
(int)vpi_get(vpiType, item));
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fstWriterSetScope(dump_file, type, name, NULL);
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
|
||||||
|
{
|
||||||
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||||
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||||
|
vpiHandle item;
|
||||||
|
s_vpi_value value;
|
||||||
|
unsigned depth = 0;
|
||||||
|
|
||||||
|
if (dump_file == 0) {
|
||||||
|
open_dumpfile(callh);
|
||||||
|
if (dump_file == 0) {
|
||||||
|
if (argv) vpi_free_object(argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (install_dumpvars_callback()) {
|
||||||
|
if (argv) vpi_free_object(argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the depth if it exists. */
|
||||||
|
if (argv) {
|
||||||
|
value.format = vpiIntVal;
|
||||||
|
vpi_get_value(vpi_scan(argv), &value);
|
||||||
|
depth = value.value.integer;
|
||||||
|
}
|
||||||
|
if (!depth) depth = 10000;
|
||||||
|
|
||||||
|
/* This dumps all the modules in the design if none are given. */
|
||||||
|
if (!argv || !(item = vpi_scan(argv))) {
|
||||||
|
argv = vpi_iterate(vpiModule, 0x0);
|
||||||
|
assert(argv); /* There must be at least one top level module. */
|
||||||
|
item = vpi_scan(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ; item; item = vpi_scan(argv)) {
|
||||||
|
char *scname;
|
||||||
|
const char *fullname;
|
||||||
|
int add_var = 0;
|
||||||
|
int dep;
|
||||||
|
PLI_INT32 item_type = vpi_get(vpiType, item);
|
||||||
|
|
||||||
|
/* If this is a signal make sure it has not already
|
||||||
|
* been included. */
|
||||||
|
switch (item_type) {
|
||||||
|
case vpiIntegerVar:
|
||||||
|
case vpiMemoryWord:
|
||||||
|
case vpiNamedEvent:
|
||||||
|
case vpiNet:
|
||||||
|
case vpiParameter:
|
||||||
|
case vpiRealVar:
|
||||||
|
case vpiReg:
|
||||||
|
case vpiTimeVar:
|
||||||
|
/* Warn if the variables scope (which includes the
|
||||||
|
* variable) or the variable itself was already
|
||||||
|
* included. A scope does not automatically include
|
||||||
|
* memory words so do not check the scope for them. */
|
||||||
|
scname = strdup(vpi_get_str(vpiFullName,
|
||||||
|
vpi_handle(vpiScope, item)));
|
||||||
|
fullname = vpi_get_str(vpiFullName, item);
|
||||||
|
if (((item_type != vpiMemoryWord) &&
|
||||||
|
vcd_names_search(&fst_tab, scname)) ||
|
||||||
|
vcd_names_search(&fst_var, fullname)) {
|
||||||
|
vpi_printf("FST warning: skipping signal %s, "
|
||||||
|
"it was previously included.\n",
|
||||||
|
fullname);
|
||||||
|
free(scname);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
add_var = 1;
|
||||||
|
}
|
||||||
|
free(scname);
|
||||||
|
}
|
||||||
|
|
||||||
|
dep = draw_scope(item, callh);
|
||||||
|
|
||||||
|
scan_item(depth, item, 0);
|
||||||
|
|
||||||
|
while (dep--) fstWriterSetUpscope(dump_file);
|
||||||
|
|
||||||
|
/* Add this signal to the variable list so we can verify it
|
||||||
|
* is not included twice. This must be done after it has
|
||||||
|
* been added */
|
||||||
|
if (add_var) {
|
||||||
|
vcd_names_add(&fst_var, vpi_get_str(vpiFullName, item));
|
||||||
|
vcd_names_sort(&fst_var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sys_fst_register()
|
||||||
|
{
|
||||||
|
s_vpi_systf_data tf_data;
|
||||||
|
vpiHandle res;
|
||||||
|
|
||||||
|
/* All the compiletf routines are located in vcd_priv.c. */
|
||||||
|
|
||||||
|
tf_data.type = vpiSysTask;
|
||||||
|
tf_data.tfname = "$dumpall";
|
||||||
|
tf_data.calltf = sys_dumpall_calltf;
|
||||||
|
tf_data.compiletf = sys_no_arg_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.user_data = "$dumpall";
|
||||||
|
res = vpi_register_systf(&tf_data);
|
||||||
|
vpip_make_systf_system_defined(res);
|
||||||
|
|
||||||
|
tf_data.type = vpiSysTask;
|
||||||
|
tf_data.tfname = "$dumpfile";
|
||||||
|
tf_data.calltf = sys_dumpfile_calltf;
|
||||||
|
tf_data.compiletf = sys_one_string_arg_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.user_data = "$dumpfile";
|
||||||
|
res = vpi_register_systf(&tf_data);
|
||||||
|
vpip_make_systf_system_defined(res);
|
||||||
|
|
||||||
|
tf_data.type = vpiSysTask;
|
||||||
|
tf_data.tfname = "$dumpflush";
|
||||||
|
tf_data.calltf = sys_dumpflush_calltf;
|
||||||
|
tf_data.compiletf = sys_no_arg_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.user_data = "$dumpflush";
|
||||||
|
res = vpi_register_systf(&tf_data);
|
||||||
|
vpip_make_systf_system_defined(res);
|
||||||
|
|
||||||
|
tf_data.type = vpiSysTask;
|
||||||
|
tf_data.tfname = "$dumplimit";
|
||||||
|
tf_data.calltf = sys_dumplimit_calltf;
|
||||||
|
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.user_data = "$dumplimit";
|
||||||
|
res = vpi_register_systf(&tf_data);
|
||||||
|
vpip_make_systf_system_defined(res);
|
||||||
|
|
||||||
|
tf_data.type = vpiSysTask;
|
||||||
|
tf_data.tfname = "$dumpoff";
|
||||||
|
tf_data.calltf = sys_dumpoff_calltf;
|
||||||
|
tf_data.compiletf = sys_no_arg_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.user_data = "$dumpoff";
|
||||||
|
res = vpi_register_systf(&tf_data);
|
||||||
|
vpip_make_systf_system_defined(res);
|
||||||
|
|
||||||
|
tf_data.type = vpiSysTask;
|
||||||
|
tf_data.tfname = "$dumpon";
|
||||||
|
tf_data.calltf = sys_dumpon_calltf;
|
||||||
|
tf_data.compiletf = sys_no_arg_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.user_data = "$dumpon";
|
||||||
|
res = vpi_register_systf(&tf_data);
|
||||||
|
vpip_make_systf_system_defined(res);
|
||||||
|
|
||||||
|
tf_data.type = vpiSysTask;
|
||||||
|
tf_data.tfname = "$dumpvars";
|
||||||
|
tf_data.calltf = sys_dumpvars_calltf;
|
||||||
|
tf_data.compiletf = sys_dumpvars_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.user_data = "$dumpvars";
|
||||||
|
res = vpi_register_systf(&tf_data);
|
||||||
|
vpip_make_systf_system_defined(res);
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com)
|
* Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com)
|
||||||
*
|
*
|
||||||
* This source code is free software; you can redistribute it
|
* This source code is free software; you can redistribute it
|
||||||
* and/or modify it in source code form under the terms of the GNU
|
* and/or modify it in source code form under the terms of the GNU
|
||||||
|
|
@ -47,9 +47,11 @@ extern void sys_lxt_register();
|
||||||
static void sys_lxt_register() { fputs("LXT support disabled since libbzip2 not available\n",stderr); exit(1); }
|
static void sys_lxt_register() { fputs("LXT support disabled since libbzip2 not available\n",stderr); exit(1); }
|
||||||
#endif
|
#endif
|
||||||
extern void sys_lxt2_register();
|
extern void sys_lxt2_register();
|
||||||
|
extern void sys_fst_register();
|
||||||
#else
|
#else
|
||||||
static void sys_lxt_register() { fputs("LXT support disabled since zlib not available\n",stderr); exit(1); }
|
static void sys_lxt_register() { fputs("LXT support disabled since zlib not available\n",stderr); exit(1); }
|
||||||
static void sys_lxt2_register() { fputs("LXT2 support disabled since zlib not available\n",stderr); exit(1); }
|
static void sys_lxt2_register() { fputs("LXT2 support disabled since zlib not available\n",stderr); exit(1); }
|
||||||
|
static void sys_fst_register() { fputs("FST support disabled since zlib not available\n",stderr); exit(1); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void sys_lxt_or_vcd_register()
|
static void sys_lxt_or_vcd_register()
|
||||||
|
|
@ -78,7 +80,13 @@ static void sys_lxt_or_vcd_register()
|
||||||
|
|
||||||
for (idx = 0 ; idx < vlog_info.argc ; idx += 1) {
|
for (idx = 0 ; idx < vlog_info.argc ; idx += 1) {
|
||||||
|
|
||||||
if (strcmp(vlog_info.argv[idx],"-lxt") == 0) {
|
if (strcmp(vlog_info.argv[idx],"-fst") == 0) {
|
||||||
|
dumper = "fst";
|
||||||
|
|
||||||
|
} else if (strcmp(vlog_info.argv[idx],"-fst-none") == 0) {
|
||||||
|
dumper = "none";
|
||||||
|
|
||||||
|
} else if (strcmp(vlog_info.argv[idx],"-lxt") == 0) {
|
||||||
dumper = "lxt";
|
dumper = "lxt";
|
||||||
|
|
||||||
} else if (strcmp(vlog_info.argv[idx],"-lxt-space") == 0) {
|
} else if (strcmp(vlog_info.argv[idx],"-lxt-space") == 0) {
|
||||||
|
|
@ -135,6 +143,12 @@ static void sys_lxt_or_vcd_register()
|
||||||
else if (strcmp(dumper, "VCD") == 0)
|
else if (strcmp(dumper, "VCD") == 0)
|
||||||
sys_vcd_register();
|
sys_vcd_register();
|
||||||
|
|
||||||
|
else if (strcmp(dumper, "fst") == 0)
|
||||||
|
sys_fst_register();
|
||||||
|
|
||||||
|
else if (strcmp(dumper, "FST") == 0)
|
||||||
|
sys_fst_register();
|
||||||
|
|
||||||
else if (strcmp(dumper, "lxt") == 0)
|
else if (strcmp(dumper, "lxt") == 0)
|
||||||
sys_lxt_register();
|
sys_lxt_register();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.TH vvp 1 "April 17th, 2009" "" "Version %M.%m.%n %E"
|
.TH vvp 1 "May 21st, 2010" "" "Version %M.%m.%n %E"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
vvp - Icarus Verilog vvp runtime engine
|
vvp - Icarus Verilog vvp runtime engine
|
||||||
|
|
||||||
|
|
@ -102,10 +102,15 @@ space, and is written out incrementally. Thus, you can view lxt2 files
|
||||||
while a simulation is still running (or paused) or if your simulation
|
while a simulation is still running (or paused) or if your simulation
|
||||||
crashes or is killed, you still have a useful dump.
|
crashes or is killed, you still have a useful dump.
|
||||||
|
|
||||||
|
.TP 8
|
||||||
|
.B -fst
|
||||||
|
A modern dumping format this is both faster and more compact than the
|
||||||
|
other dumping formats.
|
||||||
|
|
||||||
.TP 8
|
.TP 8
|
||||||
.B -none
|
.B -none
|
||||||
This flag can be used by itself or appended to the end of the above
|
This flag can be used by itself or appended to the end of the above
|
||||||
dumpers (vcd/lxt/lxt2/lx2) to suppress all waveform output. This can
|
dumpers (vcd/lxt/lxt2/lx2/fst) to suppress all waveform output. This can
|
||||||
make long simulations run faster.
|
make long simulations run faster.
|
||||||
|
|
||||||
.TP 8
|
.TP 8
|
||||||
|
|
@ -128,7 +133,7 @@ The vvp command also accepts some environment variables that control
|
||||||
its behavior. These can be used to make semi-permanent changes.
|
its behavior. These can be used to make semi-permanent changes.
|
||||||
|
|
||||||
.TP 8
|
.TP 8
|
||||||
.B IVERILOG_DUMPER=\fIlxt|lxt2|lx2|vcd|none\fP
|
.B IVERILOG_DUMPER=\fIfst|lxt|lxt2|lx2|vcd|none\fP
|
||||||
This selects the output format for the waveform output. Normally,
|
This selects the output format for the waveform output. Normally,
|
||||||
waveforms are dumped in vcd format, but this variable can be used to
|
waveforms are dumped in vcd format, but this variable can be used to
|
||||||
select lxt format, which is far more compact, though limited to
|
select lxt format, which is far more compact, though limited to
|
||||||
|
|
@ -158,7 +163,7 @@ iverilog\-vpi(1),
|
||||||
|
|
||||||
.SH COPYRIGHT
|
.SH COPYRIGHT
|
||||||
.nf
|
.nf
|
||||||
Copyright \(co 2001\-2009 Stephen Williams
|
Copyright \(co 2001\-2010 Stephen Williams
|
||||||
|
|
||||||
This document can be freely redistributed according to the terms of the
|
This document can be freely redistributed according to the terms of the
|
||||||
GNU General Public License version 2.0
|
GNU General Public License version 2.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue