URL
https://opencores.org/ocsvn/eco32/eco32/trunk
Subversion Repositories eco32
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 283 to Rev 284
- ↔ Reverse comparison
Rev 283 → Rev 284
/eco32/trunk/tools/vcdchk/vcdchk.c
0,0 → 1,1710
/* |
* vcdchk.c -- check the value of a signal in a VCD file |
*/ |
|
|
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <stdarg.h> |
#include <ctype.h> |
|
|
#define MAX_LINE 200 |
#define MAX_NAME 1000 |
|
|
/**************************************************************/ |
|
|
typedef int Bool; |
|
#define FALSE 0 |
#define TRUE 1 |
|
|
/**************************************************************/ |
|
|
Bool debugVCD = FALSE; |
Bool debugCHK = FALSE; |
Bool debugCheck = FALSE; |
|
|
/**************************************************************/ |
|
|
void error(char *fmt, ...) { |
va_list ap; |
|
va_start(ap, fmt); |
printf("Error: "); |
vprintf(fmt, ap); |
printf("\n"); |
va_end(ap); |
exit(1); |
} |
|
|
void *allocate(int size) { |
void *res; |
|
res = malloc(size); |
if (res == NULL) { |
error("no memory"); |
} |
return res; |
} |
|
|
void release(void *p) { |
if (p == NULL) { |
error("NULL pointer in release()"); |
} |
free(p); |
} |
|
|
/**************************************************************/ |
|
|
#define KW_COMMENT 0 |
#define KW_DATE 1 |
#define KW_DUMPALL 2 |
#define KW_DUMPOFF 3 |
#define KW_DUMPON 4 |
#define KW_DUMPVARS 5 |
#define KW_END 6 |
#define KW_ENDDEFS 7 |
#define KW_SCOPE 8 |
#define KW_TIMESCALE 9 |
#define KW_UPSCOPE 10 |
#define KW_VAR 11 |
#define KW_VERSION 12 |
|
|
typedef struct { |
char *name; |
int number; |
} Keyword; |
|
|
static Keyword keywords[] = { |
{ "comment", KW_COMMENT }, |
{ "date", KW_DATE }, |
{ "dumpall", KW_DUMPALL }, |
{ "dumpoff", KW_DUMPOFF }, |
{ "dumpon", KW_DUMPON }, |
{ "dumpvars", KW_DUMPVARS }, |
{ "end", KW_END }, |
{ "enddefinitions", KW_ENDDEFS }, |
{ "scope", KW_SCOPE }, |
{ "timescale", KW_TIMESCALE }, |
{ "upscope", KW_UPSCOPE }, |
{ "var", KW_VAR }, |
{ "version", KW_VERSION }, |
}; |
|
|
Keyword *lookupKeyword(char *name) { |
int lo, hi, tst; |
int res; |
|
lo = 0; |
hi = sizeof(keywords) / sizeof(keywords[0]) - 1; |
while (lo <= hi) { |
tst = (lo + hi) / 2; |
res = strcmp(keywords[tst].name, name); |
if (res == 0) { |
return &keywords[tst]; |
} |
if (res < 0) { |
lo = tst + 1; |
} else { |
hi = tst - 1; |
} |
} |
return NULL; |
} |
|
|
/**************************************************************/ |
|
|
#define VT_EVENT 0 |
#define VT_INTEGER 1 |
#define VT_PARAMETER 2 |
#define VT_REAL 3 |
#define VT_REG 4 |
#define VT_SUPPLY0 5 |
#define VT_SUPPLY1 6 |
#define VT_TIME 7 |
#define VT_TRI 8 |
#define VT_TRI0 9 |
#define VT_TRI1 10 |
#define VT_TRIAND 11 |
#define VT_TRIOR 12 |
#define VT_TRIREG 13 |
#define VT_WAND 14 |
#define VT_WIRE 15 |
#define VT_WOR 16 |
|
|
typedef struct { |
char *name; |
int number; |
} VarType; |
|
|
static VarType varTypes[] = { |
{ "event", VT_EVENT }, |
{ "integer", VT_INTEGER }, |
{ "parameter", VT_PARAMETER }, |
{ "real", VT_REAL }, |
{ "reg", VT_REG }, |
{ "supply0", VT_SUPPLY0 }, |
{ "supply1", VT_SUPPLY1 }, |
{ "time", VT_TIME }, |
{ "tri", VT_TRI }, |
{ "tri0", VT_TRI0 }, |
{ "tri1", VT_TRI1 }, |
{ "triand", VT_TRIAND }, |
{ "trior", VT_TRIOR }, |
{ "trireg", VT_TRIREG }, |
{ "wand", VT_WAND }, |
{ "wire", VT_WIRE }, |
{ "wor", VT_WOR }, |
}; |
|
|
VarType *lookupVarType(char *name) { |
int lo, hi, tst; |
int res; |
|
lo = 0; |
hi = sizeof(varTypes) / sizeof(varTypes[0]) - 1; |
while (lo <= hi) { |
tst = (lo + hi) / 2; |
res = strcmp(varTypes[tst].name, name); |
if (res == 0) { |
return &varTypes[tst]; |
} |
if (res < 0) { |
lo = tst + 1; |
} else { |
hi = tst - 1; |
} |
} |
return NULL; |
} |
|
|
/**************************************************************/ |
|
|
typedef struct change { |
int code; /* id code of variable */ |
char *vectorValue; /* vector value stored as string */ |
/* NULL if value is scalar value */ |
char scalarValue; /* one of '0', '1', 'x', or 'z' */ |
/* '\0' if value is vector value */ |
struct change *next; /* next value change in this step */ |
} Change; |
|
|
Change *newChange(int code, char *vectorValue, char scalarValue) { |
Change *change; |
|
change = allocate(sizeof(Change)); |
change->code = code; |
if (vectorValue != NULL) { |
change->vectorValue = allocate(strlen(vectorValue) + 1); |
strcpy(change->vectorValue, vectorValue); |
change->scalarValue = '\0'; |
} else { |
change->vectorValue = NULL; |
change->scalarValue = scalarValue; |
} |
change->next = NULL; |
return change; |
} |
|
|
typedef struct step { |
int time; /* simulation time for this step */ |
struct step *prev; /* previous time step */ |
struct step *next; /* next time step */ |
Change *changes; /* variables that changed in this step */ |
} Step; |
|
|
Step *newStep(int time, Step *prev) { |
Step *step; |
|
step = allocate(sizeof(Step)); |
step->time = time; |
step->prev = prev; |
step->next = NULL; |
step->changes = NULL; |
return step; |
} |
|
|
typedef struct var { |
int type; /* one of VT_xxx */ |
int size; /* number of bits */ |
int code; /* id code of variable */ |
char *name; /* local name of variable */ |
int msi; /* most significant index */ |
int lsi; /* least significant index */ |
struct var *next; /* linked list of vars in scope */ |
} Var; |
|
|
Var *newVar(int type, int size, int code, |
char *name, int msi, int lsi) { |
Var *var; |
|
var = allocate(sizeof(Var)); |
var->type = type; |
var->size = size; |
var->code = code; |
var->name = allocate(strlen(name) + 1); |
strcpy(var->name, name); |
var->msi = msi; |
var->lsi = lsi; |
var->next = NULL; |
return var; |
} |
|
|
#define SCOPE_MODULE 0 |
#define SCOPE_TASK 1 |
#define SCOPE_FUNCTION 2 |
#define SCOPE_BEGIN 3 |
#define SCOPE_FORK 4 |
|
typedef struct scope { |
int number; /* every scope has a unique number */ |
int type; /* one of SCOPE_xxx */ |
char *name; /* the name of the scope */ |
struct scope *parent; |
struct scope *siblings; |
struct scope *children; |
struct scope *lastchild; |
Var *variables; |
Var *lastvar; |
} Scope; |
|
|
Scope *newScope(int type, char *name, Scope *parent) { |
static int scopeNumber = 0; |
Scope *scope; |
|
scope = allocate(sizeof(Scope)); |
scope->number = scopeNumber++; |
scope->type = type; |
scope->name = allocate(strlen(name) + 1); |
strcpy(scope->name, name); |
scope->parent = parent; |
scope->siblings = NULL; |
scope->children = NULL; |
scope->lastchild = NULL; |
scope->variables = NULL; |
scope->lastvar = NULL; |
return scope; |
} |
|
|
#define VCD_TS_S 0 |
#define VCD_TS_MS 1 |
#define VCD_TS_US 2 |
#define VCD_TS_NS 3 |
#define VCD_TS_PS 4 |
#define VCD_TS_FS 5 |
|
typedef struct { |
char *date; /* generation date */ |
char *version; /* simulator version */ |
int timescaleMult; /* 1, 10, 100 */ |
int timescaleUnit; /* one of VCD_TS_xx */ |
Scope *root; /* root scope */ |
Step *steps; /* time steps */ |
} VCD; |
|
|
/**************************************************************/ |
|
|
#define INITIAL_HASH_SIZE 100 |
|
|
typedef struct entry { |
char *name; /* key */ |
Var *var; /* value */ |
unsigned hashValue; /* hash value of name */ |
struct entry *next; /* bucket chaining */ |
} Entry; |
|
|
static int hashSize = 0; |
static Entry **buckets; |
static int numEntries; |
|
|
static unsigned hash(char *s) { |
unsigned h, g; |
|
h = 0; |
while (*s != '\0') { |
h = (h << 4) + *s++; |
g = h & 0xF0000000; |
if (g != 0) { |
h ^= g >> 24; |
h ^= g; |
} |
} |
return h; |
} |
|
|
static Bool isPrime(int i) { |
int t; |
|
if (i < 2) { |
return FALSE; |
} |
if (i == 2) { |
return TRUE; |
} |
if (i % 2 == 0) { |
return FALSE; |
} |
t = 3; |
while (t * t <= i) { |
if (i % t == 0) { |
return FALSE; |
} |
t += 2; |
} |
return TRUE; |
} |
|
|
static void initTable(void) { |
int i; |
|
hashSize = INITIAL_HASH_SIZE; |
while (!isPrime(hashSize)) { |
hashSize++; |
} |
buckets = (Entry **) allocate(hashSize * sizeof(Entry *)); |
for (i = 0; i < hashSize; i++) { |
buckets[i] = NULL; |
} |
numEntries = 0; |
} |
|
|
static void growTable(void) { |
int newHashSize; |
Entry **newBuckets; |
int i, n; |
Entry *p, *q; |
|
/* compute new hash size */ |
newHashSize = 2 * hashSize + 1; |
while (!isPrime(newHashSize)) { |
newHashSize += 2; |
} |
/* init new hash table */ |
newBuckets = (Entry **) allocate(newHashSize * sizeof(Entry *)); |
for (i = 0; i < newHashSize; i++) { |
newBuckets[i] = NULL; |
} |
/* rehash old entries */ |
for (i = 0; i < hashSize; i++) { |
p = buckets[i]; |
while (p != NULL) { |
q = p; |
p = p->next; |
n = q->hashValue % newHashSize; |
q->next = newBuckets[n]; |
newBuckets[n] = q; |
} |
} |
/* swap tables */ |
release(buckets); |
buckets = newBuckets; |
hashSize = newHashSize; |
} |
|
|
Var *lookupVar(char *name) { |
unsigned hashValue; |
int n; |
Entry *p; |
|
/* check for no table at all */ |
if (hashSize == 0) { |
/* not found */ |
return NULL; |
} |
/* compute hash value and bucket number */ |
hashValue = hash(name); |
n = hashValue % hashSize; |
/* search in bucket list */ |
p = buckets[n]; |
while (p != NULL) { |
if (p->hashValue == hashValue) { |
if (strcmp(p->name, name) == 0) { |
/* found: return variable */ |
return p->var; |
} |
} |
p = p->next; |
} |
/* not found */ |
return NULL; |
} |
|
|
void enterVar(char *name, Var *var) { |
char nameBuffer[500]; |
unsigned hashValue; |
int n; |
Entry *p; |
|
/* concat module name and variable name */ |
strcpy(nameBuffer, name); |
strcat(nameBuffer, var->name); |
/* initialize hash table if necessary */ |
if (hashSize == 0) { |
initTable(); |
} |
/* grow hash table if necessary */ |
if (numEntries == hashSize) { |
growTable(); |
} |
/* compute hash value and bucket number */ |
hashValue = hash(nameBuffer); |
n = hashValue % hashSize; |
/* add new variable to bucket list */ |
p = (Entry *) allocate(sizeof(Entry)); |
p->name = (char *) allocate(strlen(nameBuffer) + 1); |
strcpy(p->name, nameBuffer); |
p->var = var; |
p->hashValue = hashValue; |
p->next = buckets[n]; |
buckets[n] = p; |
numEntries++; |
} |
|
|
/**************************************************************/ |
|
|
static FILE *vcdFile; |
static int vcdLine; |
|
|
Bool skipBlanks(void) { |
int c; |
|
do { |
c = fgetc(vcdFile); |
if (c == EOF) { |
break; |
} |
if (c == '\n') { |
vcdLine++; |
} |
} while (c == ' ' || c == '\t' || c == '\n'); |
if (c != EOF) { |
ungetc(c, vcdFile); |
} |
return c == EOF; |
} |
|
|
Keyword *getKeyword(void) { |
int c; |
char buf[MAX_LINE]; |
char *p; |
|
c = fgetc(vcdFile); |
if (c == EOF) { |
return NULL; |
} |
if (c != '$') { |
ungetc(c, vcdFile); |
return NULL; |
} |
c = fgetc(vcdFile); |
if (c == EOF) { |
return NULL; |
} |
p = buf; |
while (isalpha(c)) { |
*p++ = c; |
c = fgetc(vcdFile); |
} |
ungetc(c, vcdFile); |
*p = '\0'; |
return lookupKeyword(buf); |
} |
|
|
void skipPastEnd(void) { |
int c; |
Keyword *kwp; |
|
while (1) { |
do { |
c = fgetc(vcdFile); |
if (c == EOF) { |
error("unexpected end of VCD file"); |
} |
if (c == '\n') { |
vcdLine++; |
} |
} while (c != '$'); |
ungetc(c, vcdFile); |
kwp = getKeyword(); |
if (kwp != NULL && kwp->number == KW_END) { |
return; |
} |
} |
} |
|
|
int collectTillDollar(char *buffer) { |
char *p; |
int c; |
|
p = buffer; |
do { |
c = fgetc(vcdFile); |
if (c == EOF) { |
error("unexpected end of VCD file"); |
} |
if (c == '\n') { |
vcdLine++; |
} |
*p++ = c; |
} while (c != '$'); |
ungetc(c, vcdFile); |
p -= 2; |
while (*p == ' ' || *p == '\t' || *p == '\n') { |
p--; |
} |
*++p = '\0'; |
return p - buffer; |
} |
|
|
int getString(char *buffer) { |
char *p; |
int c; |
|
p = buffer; |
while (1) { |
c = fgetc(vcdFile); |
if (c == EOF) { |
error("unexpected end of VCD file"); |
} |
if (c == ' ' || c == '\t' || c == '\n') { |
break; |
} |
*p++ = c; |
} |
ungetc(c, vcdFile); |
*p = '\0'; |
return p - buffer; |
} |
|
|
Bool getIdent(char *buffer) { |
char *p; |
int c; |
|
c = fgetc(vcdFile); |
if (c == EOF) { |
return FALSE; |
} |
if (!isalpha(c) && c != '_') { |
ungetc(c, vcdFile); |
return FALSE; |
} |
p = buffer; |
while (isalnum(c) || c == '_' || c == '$') { |
*p++ = c; |
c = fgetc(vcdFile); |
} |
if (c != EOF) { |
ungetc(c, vcdFile); |
} |
*p = '\0'; |
return TRUE; |
} |
|
|
int getVarCode(void) { |
int varCode; |
int weight; |
int c; |
int i; |
|
varCode = 0; |
weight = 1; |
for (i = 0; i <= 4; i++) { |
c = fgetc(vcdFile); |
if (c < '!' || c > '~') { |
break; |
} |
if (i == 4) { |
error("line %d: too many base-94 digits", vcdLine); |
} |
varCode += weight * (c - '!'); |
weight *= 94; |
} |
if (i == 0) { |
error("line %d: variable code expected", vcdLine); |
} |
if (c != EOF) { |
ungetc(c, vcdFile); |
} |
return varCode; |
} |
|
|
int getInteger(void) { |
int val; |
int c; |
|
val = 0; |
skipBlanks(); |
c = fgetc(vcdFile); |
if (!isdigit(c)) { |
error("line %d: number expected", vcdLine); |
} |
while (1) { |
if (!isdigit(c)) { |
break; |
} |
val = val * 10 + (c - '0'); |
c = fgetc(vcdFile); |
} |
if (c != EOF) { |
ungetc(c, vcdFile); |
} |
return val; |
} |
|
|
void getIndexRange(int *msip, int *lsip) { |
int c; |
|
*msip = -1; |
*lsip = -1; |
skipBlanks(); |
c = fgetc(vcdFile); |
if (c == EOF) { |
return; |
} |
if (c != '[') { |
ungetc(c, vcdFile); |
return; |
} |
*msip = getInteger(); |
skipBlanks(); |
c = fgetc(vcdFile); |
if (c != ':' && c != ']') { |
error("line %d: ':' or ']' expected", vcdLine); |
} |
if (c == ':') { |
skipBlanks(); |
*lsip = getInteger(); |
skipBlanks(); |
c = fgetc(vcdFile); |
} |
if (c != ']') { |
error("line %d: ']' expected", vcdLine); |
} |
} |
|
|
int getTime(void) { |
int c; |
|
c = fgetc(vcdFile); |
if (c != '#') { |
ungetc(c, vcdFile); |
return -1; |
} |
return getInteger(); |
} |
|
|
Change *getChange(void) { |
int c; |
int varCode; |
Change *change; |
char buffer[MAX_LINE]; |
char *p; |
|
c = fgetc(vcdFile); |
if (c != '0' && c != '1' && c != 'x' && c != 'X' && |
c != 'z' && c != 'Z' && c != 'b' && c != 'B') { |
if (c == 'r' || c == 'R') { |
error("line %d: real numbers are not supported, sorry", vcdLine); |
} |
if (c != EOF) { |
ungetc(c, vcdFile); |
} |
return NULL; |
} |
if (c == 'b' || c == 'B') { |
/* vector */ |
p = buffer; |
do { |
c = fgetc(vcdFile); |
switch (c) { |
case '0': |
case '1': |
case 'x': |
case 'z': |
*p++ = c; |
break; |
case 'X': |
case 'Z': |
*p++ = tolower(c); |
break; |
default: |
break; |
} |
} while (c == '0' || c == '1' || c == 'x' || |
c == 'X' || c == 'z' || c == 'Z'); |
if (c != EOF) { |
ungetc(c, vcdFile); |
} |
*p = '\0'; |
skipBlanks(); |
varCode = getVarCode(); |
change = newChange(varCode, buffer, '\0'); |
} else { |
/* scalar */ |
if (c == 'X') { |
c = 'x'; |
} else |
if (c == 'Z') { |
c = 'z'; |
} |
varCode = getVarCode(); |
change = newChange(varCode, NULL, c); |
} |
return change; |
} |
|
|
VCD *parseVCD(FILE *input) { |
VCD *vcd; |
int section; |
Keyword *kwp; |
char buffer[MAX_LINE]; |
int length; |
char *endptr; |
Scope *currentScope; |
Scope *scope; |
int scopeType; |
char currentPrefix[MAX_NAME]; |
VarType *vtp; |
int varType; |
int varSize; |
int varCode; |
int msi; |
int lsi; |
Var *var; |
Step *currentStep; |
Step *step; |
int time; |
Change *currentChange; |
Change *change; |
|
/* allocate a VCD structure */ |
vcd = allocate(sizeof(VCD)); |
vcd->date = NULL; |
vcd->version = NULL; |
vcd->timescaleMult = -1; |
vcd->timescaleUnit = -1; |
vcd->root = NULL; |
vcd->steps = NULL; |
/* start parsing */ |
vcdFile = input; |
vcdLine = 1; |
section = 1; |
/* read header and node information */ |
currentScope = NULL; |
currentPrefix[0] = '\0'; |
while (section == 1) { |
skipBlanks(); |
kwp = getKeyword(); |
if (kwp == NULL) { |
error("line %d: keyword expected", vcdLine); |
} |
switch (kwp->number) { |
case KW_COMMENT: |
/* ignore comment */ |
skipPastEnd(); |
break; |
case KW_DATE: |
/* record date as string */ |
skipBlanks(); |
length = collectTillDollar(buffer); |
vcd->date = allocate(length + 1); |
strcpy(vcd->date, buffer); |
skipPastEnd(); |
break; |
case KW_VERSION: |
/* record version as string */ |
skipBlanks(); |
length = collectTillDollar(buffer); |
vcd->version = allocate(length + 1); |
strcpy(vcd->version, buffer); |
skipPastEnd(); |
break; |
case KW_TIMESCALE: |
/* record timescale as multiplier and unit */ |
skipBlanks(); |
length = collectTillDollar(buffer); |
/* get multiplier */ |
vcd->timescaleMult = strtol(buffer, &endptr, 10); |
if (vcd->timescaleMult != 1 && |
vcd->timescaleMult != 10 && |
vcd->timescaleMult != 100) { |
error("line %d: illegal timescale multiplier (%d)", |
vcdLine, vcd->timescaleMult); |
} |
/* multiplier and unit may be separated by whitespace */ |
while (*endptr == ' ' || *endptr == '\t') { |
endptr++; |
} |
/* get unit */ |
if (endptr[0] == 's' && endptr[1] == '\0') { |
vcd->timescaleUnit = VCD_TS_S; |
} else |
if (endptr[0] == 'm' && endptr[1] == 's' && endptr[2] == '\0') { |
vcd->timescaleUnit = VCD_TS_MS; |
} else |
if (endptr[0] == 'u' && endptr[1] == 's' && endptr[2] == '\0') { |
vcd->timescaleUnit = VCD_TS_US; |
} else |
if (endptr[0] == 'n' && endptr[1] == 's' && endptr[2] == '\0') { |
vcd->timescaleUnit = VCD_TS_NS; |
} else |
if (endptr[0] == 'p' && endptr[1] == 's' && endptr[2] == '\0') { |
vcd->timescaleUnit = VCD_TS_PS; |
} else |
if (endptr[0] == 'f' && endptr[1] == 's' && endptr[2] == '\0') { |
vcd->timescaleUnit = VCD_TS_FS; |
} else { |
error("line %d: illegal timescale unit (%s)", |
vcdLine, endptr); |
} |
skipPastEnd(); |
break; |
case KW_SCOPE: |
/* open a new scope */ |
skipBlanks(); |
/* get type */ |
getString(buffer); |
if (strcmp(buffer, "module") == 0) { |
scopeType = SCOPE_MODULE; |
} else |
if (strcmp(buffer, "task") == 0) { |
scopeType = SCOPE_TASK; |
} else |
if (strcmp(buffer, "function") == 0) { |
scopeType = SCOPE_FUNCTION; |
} else |
if (strcmp(buffer, "begin") == 0) { |
scopeType = SCOPE_BEGIN; |
} else |
if (strcmp(buffer, "fork") == 0) { |
scopeType = SCOPE_FORK; |
} else { |
error("line %d: unknown scope type '%s'", vcdLine, buffer); |
} |
skipBlanks(); |
/* get name */ |
if (!getIdent(buffer)) { |
error("line %d: identifier expected", vcdLine); |
} |
/* allocate and link a new scope */ |
scope = newScope(scopeType, buffer, currentScope); |
if (currentScope == NULL) { |
/* new scope is a top-level scope */ |
if (vcd->root == NULL) { |
/* first top-level scope */ |
vcd->root = scope; |
} else { |
/* another top-level scope */ |
/* append, use currentScope as auxiliary variable */ |
currentScope = vcd->root; |
while (currentScope->siblings != NULL) { |
currentScope = currentScope->siblings; |
} |
currentScope->siblings = scope; |
} |
} else { |
/* new scope is not a top-level scope */ |
if (currentScope->children == NULL) { |
currentScope->children = scope; |
} else { |
currentScope->lastchild->siblings = scope; |
} |
currentScope->lastchild = scope; |
} |
currentScope = scope; |
/* augment current prefix */ |
strcat(currentPrefix, currentScope->name); |
strcat(currentPrefix, "."); |
skipPastEnd(); |
break; |
case KW_UPSCOPE: |
/* close the current scope */ |
if (currentScope == NULL) { |
error("line %d: no $scope for this $upscope", vcdLine); |
} |
currentScope = currentScope->parent; |
/* prune current prefix */ |
length = strlen(currentPrefix) - 2; |
while (length >= 0 && currentPrefix[length] != '.') { |
length--; |
} |
length++; |
currentPrefix[length] = '\0'; |
skipPastEnd(); |
break; |
case KW_VAR: |
/* record a new variable */ |
skipBlanks(); |
/* get type */ |
getString(buffer); |
vtp = lookupVarType(buffer); |
if (vtp == NULL) { |
error("line %d: unknown variable type '%s'", vcdLine, buffer); |
} |
varType = vtp->number; |
skipBlanks(); |
/* get size */ |
getString(buffer); |
varSize = strtol(buffer, &endptr, 10); |
if (*endptr != '\0' || varSize <= 0) { |
error("line %d: cannot read variable size", vcdLine); |
} |
skipBlanks(); |
/* get code */ |
varCode = getVarCode(); |
skipBlanks(); |
/* get name */ |
if (!getIdent(buffer)) { |
error("line %d: identifier expected", vcdLine); |
} |
skipBlanks(); |
/* get optional index range */ |
getIndexRange(&msi, &lsi); |
/* allocate and link a new variable */ |
var = newVar(varType, varSize, varCode, buffer, msi, lsi); |
if (currentScope->variables == NULL) { |
currentScope->variables = var; |
} else { |
currentScope->lastvar->next = var; |
} |
currentScope->lastvar = var; |
/* additionally, enter variable in var table */ |
enterVar(currentPrefix, var); |
skipPastEnd(); |
break; |
case KW_ENDDEFS: |
/* end the first section of the VCD file */ |
section = 2; |
skipPastEnd(); |
break; |
default: |
error("line %d: unexpected keyword '$%s'", vcdLine, kwp->name); |
break; |
} |
} |
/* read value changes */ |
currentStep = NULL; |
currentChange = NULL; |
while (1) { |
if (skipBlanks()) { |
break; |
} |
kwp = getKeyword(); |
if (kwp != NULL) { |
switch (kwp->number) { |
case KW_COMMENT: |
/* ignore comment */ |
skipPastEnd(); |
break; |
case KW_DUMPVARS: |
case KW_DUMPALL: |
case KW_DUMPOFF: |
case KW_DUMPON: |
/* record value changes */ |
while (1) { |
skipBlanks(); |
change = getChange(); |
if (change == NULL) { |
break; |
} |
if (currentChange == NULL) { |
if (currentStep == NULL) { |
error("line %d: value change without simulation time", |
vcdLine); |
} |
currentStep->changes = change; |
} else { |
currentChange->next = change; |
} |
currentChange = change; |
} |
skipPastEnd(); |
break; |
default: |
error("line %d: unexpected keyword '%s'", vcdLine, kwp->name); |
break; |
} |
continue; |
} |
time = getTime(); |
if (time >= 0) { |
if (currentStep != NULL && currentStep->time >= time) { |
error("line %d: time step has illegal simulation time", vcdLine); |
} |
step = newStep(time, currentStep); |
if (currentStep == NULL) { |
vcd->steps = step; |
} else { |
currentStep->next = step; |
} |
currentStep = step; |
currentChange = NULL; |
continue; |
} |
change = getChange(); |
if (change != NULL) { |
if (currentChange == NULL) { |
if (currentStep == NULL) { |
error("line %d: value change without simulation time", vcdLine); |
} |
currentStep->changes = change; |
} else { |
currentChange->next = change; |
} |
currentChange = change; |
continue; |
} |
error("line %d: keyword, time, or value change expected", vcdLine); |
} |
/* done */ |
return vcd; |
} |
|
|
char *varType[] = { |
"event", |
"integer", |
"parameter", |
"real", |
"reg", |
"supply0", |
"supply1", |
"time", |
"tri", |
"tri0", |
"tri1", |
"triand", |
"trior", |
"trireg", |
"wand", |
"wire", |
"wor", |
}; |
|
|
char *scopeType[] = { |
"module", |
"task", |
"function", |
"begin", |
"fork", |
}; |
|
|
void showVarPrefix(Scope *scope) { |
if (scope == NULL) { |
return; |
} |
showVarPrefix(scope->parent); |
printf("%s.", scope->name); |
} |
|
|
void showScope(Scope *scope) { |
Var *var; |
|
printf(" scope #%d\n", scope->number); |
printf(" type : %s\n", scopeType[scope->type]); |
printf(" name : %s\n", scope->name); |
printf(" parent : "); |
if (scope->parent == NULL) { |
printf("-- none --"); |
} else { |
printf("scope #%d", scope->parent->number); |
} |
printf("\n"); |
printf(" siblings : "); |
if (scope->siblings == NULL) { |
printf("-- none --"); |
} else { |
printf("scope #%d", scope->siblings->number); |
} |
printf("\n"); |
printf(" children : "); |
if (scope->children == NULL) { |
printf("-- none --"); |
} else { |
printf("scope #%d", scope->children->number); |
} |
printf("\n"); |
printf(" lastchild : "); |
if (scope->lastchild == NULL) { |
printf("-- none --"); |
} else { |
printf("scope #%d", scope->lastchild->number); |
} |
printf("\n"); |
printf(" var prefix : "); |
showVarPrefix(scope); |
printf("\n"); |
printf(" variables : "); |
if (scope->variables == NULL) { |
printf("-- none --\n"); |
} else { |
printf("\n"); |
var = scope->variables; |
while (var != NULL) { |
printf(" "); |
printf("%s", var->name); |
if (var->msi != -1) { |
printf(" [%d", var->msi); |
if (var->lsi != -1) { |
printf(":%d", var->lsi); |
} |
printf("]"); |
} |
printf("\n"); |
printf(" "); |
printf("type = %-9s ", varType[var->type]); |
printf("size = %4d ", var->size); |
printf("code = %6d\n", var->code); |
var = var->next; |
} |
} |
if (scope->siblings != NULL) { |
showScope(scope->siblings); |
} |
if (scope->children != NULL) { |
showScope(scope->children); |
} |
} |
|
|
void showSteps(Step *steps) { |
Change *changes; |
|
while (steps != NULL) { |
printf(" #%d\n", steps->time); |
changes = steps->changes; |
while (changes != NULL) { |
printf(" code %8d : value ", changes->code); |
if (changes->vectorValue != NULL) { |
printf("'%s'", changes->vectorValue); |
} else { |
printf("'%c'", changes->scalarValue); |
} |
printf("\n"); |
changes = changes->next; |
} |
steps = steps->next; |
} |
} |
|
|
char *tsUnit[6] = { |
"s", "ms", "us", |
"ns", "ps", "fs" |
}; |
|
|
void showVCD(VCD *vcd) { |
printf("Value Change Dump Data\n"); |
|
printf("date : "); |
if (vcd->date == NULL) { |
printf("-- none --"); |
} else { |
printf("%s", vcd->date); |
} |
printf("\n"); |
|
printf("version : "); |
if (vcd->version == NULL) { |
printf("-- none --"); |
} else { |
printf("%s", vcd->version); |
} |
printf("\n"); |
|
printf("timescale : "); |
if (vcd->timescaleMult < 0 || |
vcd->timescaleUnit < 0) { |
printf("-- none --"); |
} else { |
printf("%d %s", vcd->timescaleMult, tsUnit[vcd->timescaleUnit]); |
} |
printf("\n"); |
|
printf("root scope : "); |
if (vcd->root == NULL) { |
printf("-- none --"); |
printf("\n"); |
} else { |
printf("%d", vcd->root->number); |
printf("\n"); |
showScope(vcd->root); |
} |
|
printf("value changes : "); |
if (vcd->steps == NULL) { |
printf("-- none --"); |
printf("\n"); |
} else { |
printf("\n"); |
showSteps(vcd->steps); |
} |
} |
|
|
/**************************************************************/ |
|
|
typedef struct chk { |
int timeMult; /* this many timeUnits */ |
int timeUnit; /* one of VCD_TS_xxx */ |
char *name; /* name of variable to check */ |
int width; /* width of variable to check */ |
char *value; /* value to check variable against */ |
struct chk *next; /* list elements are connected by 'and' */ |
} CHK; |
|
|
void shift(char *buffer, int width, int amount, char *val) { |
int j, i; |
|
for (j = 0; j < amount; j++) { |
for (i = 0; i < width - 1; i++) { |
buffer[i] = buffer[i + 1]; |
} |
buffer[i] = val[j]; |
} |
} |
|
|
char *bitval1[2] = { |
"0", "1" |
}; |
|
|
char *bitval4[16] = { |
"0000", "0001", "0010", "0011", |
"0100", "0101", "0110", "0111", |
"1000", "1001", "1010", "1011", |
"1100", "1101", "1110", "1111" |
}; |
|
|
int skipSpace(int c, FILE *chkFile) { |
while (1) { |
while (c == ' ' || c == '\t' || c == '\n') { |
c = fgetc(chkFile); |
} |
if (c != '/') { |
return c; |
} |
c = fgetc(chkFile); |
if (c == EOF) { |
error("chk spec: unexpected end of input"); |
} |
if (c != '/') { |
ungetc(c, chkFile); |
return '/'; |
} |
do { |
c = fgetc(chkFile); |
if (c == EOF) { |
return EOF; |
} |
} while (c != '\n'); |
} |
} |
|
|
CHK *parseSingleChk(FILE *chkFile) { |
CHK *chk; |
int c, d; |
char name[MAX_LINE]; |
char *p; |
int base; |
int i; |
|
chk = allocate(sizeof(CHK)); |
c = fgetc(chkFile); |
c = skipSpace(c, chkFile); |
chk->timeMult = 0; |
if (!isdigit(c)) { |
error("chk spec: time multiplier does not start with a digit"); |
} |
while (isdigit(c)) { |
chk->timeMult = chk->timeMult * 10 + (c - '0'); |
c = fgetc(chkFile); |
} |
c = skipSpace(c, chkFile); |
if (c == 's') { |
chk->timeUnit = VCD_TS_S; |
} else { |
d = fgetc(chkFile); |
if (c == 'm' && d == 's') { |
chk->timeUnit = VCD_TS_MS; |
} else |
if (c == 'u' && d == 's') { |
chk->timeUnit = VCD_TS_US; |
} else |
if (c == 'n' && d == 's') { |
chk->timeUnit = VCD_TS_NS; |
} else |
if (c == 'p' && d == 's') { |
chk->timeUnit = VCD_TS_PS; |
} else |
if (c == 'f' && d == 's') { |
chk->timeUnit = VCD_TS_FS; |
} else { |
error("chk spec: illegal time unit"); |
} |
} |
c = fgetc(chkFile); |
c = skipSpace(c, chkFile); |
p = name; |
if (!isalpha(c) && c != '_') { |
error("chk spec: name does not start with a letter"); |
} |
while (isalnum(c) || c == '_' || c == '$' || c == '.') { |
*p++ = c; |
c = fgetc(chkFile); |
} |
*p = '\0'; |
chk->name = allocate(p - name + 1); |
strcpy(chk->name, name); |
c = skipSpace(c, chkFile); |
chk->width = 0; |
if (!isdigit(c)) { |
error("chk spec: bit width does not start with a digit"); |
} |
while (isdigit(c)) { |
chk->width = chk->width * 10 + (c - '0'); |
c = fgetc(chkFile); |
} |
if (chk->width <= 0) { |
error("chk spec: illegal bit width"); |
} |
chk->value = allocate(chk->width + 1); |
if (c != '\'') { |
error("chk spec: apostrophe missing after bit width"); |
} |
c = fgetc(chkFile); |
if (c == 'b') { |
base = 2; |
} else |
if (c == 'h') { |
base = 16; |
} else { |
error("chk spec: illegal number base"); |
} |
c = fgetc(chkFile); |
if (c == 'x' || c == 'X') { |
for (i = 0; i < chk->width; i++) { |
chk->value[i] = 'x'; |
} |
} else |
if (c == 'z' || c == 'Z') { |
for (i = 0; i < chk->width; i++) { |
chk->value[i] = 'z'; |
} |
} else { |
for (i = 0; i < chk->width; i++) { |
chk->value[i] = '0'; |
} |
} |
chk->value[chk->width] = '\0'; |
if (base == 2) { |
if (c != 'x' && c != 'X' && |
c != 'z' && c != 'Z' && |
c != '0' && c != '1') { |
error("chk spec: base-2 number does not start with binary digit"); |
} |
while (1) { |
if (c == 'x' || c == 'X') { |
shift(chk->value, chk->width, 1, "x"); |
} else |
if (c == 'z' || c == 'Z') { |
shift(chk->value, chk->width, 1, "z"); |
} else |
if (c == '0' || c == '1') { |
shift(chk->value, chk->width, 1, bitval1[c - '0']); |
} else { |
break; |
} |
c = fgetc(chkFile); |
} |
} else |
if (base == 16) { |
if (c != 'x' && c != 'X' && |
c != 'z' && c != 'Z' && |
!isxdigit(c)) { |
error("chk spec: base-16 number does not start with hex digit"); |
} |
while (1) { |
if (c == 'x' || c == 'X') { |
shift(chk->value, chk->width, 4, "xxxx"); |
} else |
if (c == 'z' || c == 'Z') { |
shift(chk->value, chk->width, 4, "zzzz"); |
} else |
if (c >= '0' && c <= '9') { |
shift(chk->value, chk->width, 4, bitval4[c - '0']); |
} else |
if (c >= 'A' && c <= 'F') { |
shift(chk->value, chk->width, 4, bitval4[c - 'A' + 10]); |
} else |
if (c >= 'a' && c <= 'f') { |
shift(chk->value, chk->width, 4, bitval4[c - 'a' + 10]); |
} else { |
break; |
} |
c = fgetc(chkFile); |
} |
} |
c = skipSpace(c, chkFile); |
if (c != EOF) { |
ungetc(c, chkFile); |
} |
chk->next = NULL; |
return chk; |
} |
|
|
CHK *parseCHK(FILE *chkFile) { |
CHK *chk; |
CHK *cur; |
int c; |
|
chk = parseSingleChk(chkFile); |
cur = chk; |
c = fgetc(chkFile); |
while (c != EOF) { |
ungetc(c, chkFile); |
cur->next = parseSingleChk(chkFile); |
cur = cur->next; |
c = fgetc(chkFile); |
} |
return chk; |
} |
|
|
void showSingleCHK(CHK *chk) { |
printf("time mult = %d\n", chk->timeMult); |
printf("time unit = %s\n", tsUnit[chk->timeUnit]); |
printf("var name = %s\n", chk->name); |
printf("var width = %d\n", chk->width); |
printf("var value = %s\n", chk->value); |
} |
|
|
void showCHK(CHK *chk) { |
do { |
showSingleCHK(chk); |
chk = chk->next; |
if (chk != NULL) { |
printf("&&\n"); |
} |
} while (chk != NULL); |
} |
|
|
/**************************************************************/ |
|
|
#define bitEqual(v, c) ((v) == (c) || (c) == 'x') |
|
|
Bool compare(Change *change, char *check, int width) { |
char *value; |
int i, n; |
|
if (change->vectorValue != NULL) { |
/* compare two vectors */ |
value = allocate(width + 1); |
n = strlen(change->vectorValue); |
if (change->vectorValue[0] == 'x') { |
for (i = 0; i < width - n; i++) { |
value[i] = 'x'; |
} |
} else |
if (change->vectorValue[0] == 'z') { |
for (i = 0; i < width - n; i++) { |
value[i] = 'z'; |
} |
} else { |
for (i = 0; i < width - n; i++) { |
value[i] = '0'; |
} |
} |
for (i = 0; i < n; i++) { |
value[width - n + i] = change->vectorValue[i]; |
} |
value[width] = '\0'; |
if (debugCheck) { |
printf("v = '%s'\nc = '%s'\n", value, check); |
} |
for (i = 0; i < width; i++) { |
if (!bitEqual(value[i], check[i])) { |
return FALSE; |
} |
} |
return TRUE; |
} else { |
/* compare two scalars */ |
if (debugCheck) { |
printf("v = '%c'\nc = '%c'\n", change->scalarValue, check[0]); |
} |
return bitEqual(change->scalarValue, check[0]); |
} |
} |
|
|
Bool check(VCD *vcd, CHK *chk) { |
Var *var; |
int time; |
Step *step; |
Step *next; |
Change *change; |
|
/* locate variable */ |
var = lookupVar(chk->name); |
if (var == NULL) { |
error("check: variable '%s' not found", chk->name); |
} |
/* check sizes */ |
if (var->size != chk->width) { |
error("check: variable size in vcd and chk different"); |
} |
/* check time units */ |
if (vcd->timescaleUnit != chk->timeUnit) { |
error("check: time units in vcd and chk different"); |
} |
/* locate time step just before specified time */ |
time = chk->timeMult; |
step = vcd->steps; |
if (step == NULL) { |
error("check: no simulation time steps at all"); |
} |
if (time < step->time) { |
error("check: check time < first simulation time step"); |
} |
next = step->next; |
while (next != NULL && time >= next->time) { |
step = next; |
next = step->next; |
} |
/* find oldest time step in which the variable changed */ |
while (step != NULL) { |
change = step->changes; |
while (change != NULL) { |
if (change->code == var->code) { |
/* value change found */ |
return compare(change, chk->value, chk->width); |
} |
change = change->next; |
} |
step = step->prev; |
} |
error("check: variable '%s' was never dumped", chk->name); |
return FALSE; |
} |
|
|
/**************************************************************/ |
|
|
void usage(char *myself) { |
printf("Usage: %s <VCD file> <CHK file>\n", myself); |
exit(1); |
} |
|
|
int main(int argc, char *argv[]) { |
char *vcdName; |
FILE *vcdFile; |
VCD *vcd; |
char *chkName; |
FILE *chkFile; |
CHK *chk; |
|
if (argc != 3) { |
usage(argv[0]); |
} |
vcdName = argv[1]; |
vcdFile = fopen(vcdName, "r"); |
if (vcdFile == NULL) { |
error("cannot open VCD file '%s'", vcdName); |
} |
vcd = parseVCD(vcdFile); |
fclose(vcdFile); |
if (debugVCD) { |
showVCD(vcd); |
} |
chkName = argv[2]; |
chkFile = fopen(chkName, "r"); |
if (chkFile == NULL) { |
error("cannot open CHK file '%s'", chkName); |
} |
chk = parseCHK(chkFile); |
if (debugCHK) { |
showCHK(chk); |
} |
fclose(chkFile); |
while (chk != NULL) { |
if (!check(vcd, chk)) { |
printf("failure, check:\n"); |
showSingleCHK(chk); |
return 1; |
} |
chk = chk->next; |
} |
printf("success\n"); |
return 0; |
} |
/eco32/trunk/tools/vcdchk/ref.vcd
0,0 → 1,55
$date |
June 26, 1998 10:05:41 |
$end |
$version |
VERILOG-XL 2.7 |
$end |
|
$timescale |
1 ns |
$end |
|
$scope module top $end |
$scope module m1 $end |
$var trireg 1 *@ net1 $end |
$var trireg 1 *# net2 $end |
$var trireg 1 *$ net3 $end |
$upscope $end |
$scope task t1 $end |
$var reg 32 (k accumulator[31:0] $end |
$var integer 32 {2 index $end |
$upscope $end |
$upscope $end |
$enddefinitions $end |
$comment |
Note: $dumpvars was executed at time ’#500’. |
All initial values are dumped at this time. |
$end |
|
#500 |
$dumpvars x*@ x*# x*$ bx (k bx {2 $end |
|
#505 |
0*@ |
1*# |
1*$ |
b10zx1110x11100 (k b1111000101z01x {2 |
#510 |
0*$ |
|
#520 |
1*$ |
#530 |
0*$ |
bz (k |
#535 |
$dumpall 0*@ 1*# 0*$ |
bz (k b1111000101z01x {2 $end |
#540 |
1*$ |
#1000 |
$dumpoff x*@ x*# x*$ bx (k bx {2 $end |
#2000 |
$dumpon z*@ 1*# 0*$ b0 (k bx {2 $end |
#2010 |
1*$ |
/eco32/trunk/tools/vcdchk/ref-fail.chk
0,0 → 1,3
519 ns |
top.t1.accumulator |
32'b10zx1010x11xx0 |
/eco32/trunk/tools/vcdchk/ref-succ.chk
0,0 → 1,3
519 ns |
top.t1.accumulator |
32'b10zx1110x11xx0 |
/eco32/trunk/tools/vcdchk/Makefile
0,0 → 1,17
# |
# Makefile for VCD check utility |
# |
|
BUILD = ../../build |
|
all: vcdchk |
|
install: vcdchk |
mkdir -p $(BUILD)/bin |
cp vcdchk $(BUILD)/bin |
|
vcdchk: vcdchk.c |
gcc -m32 -g -Wall -o vcdchk vcdchk.c |
|
clean: |
rm -f *~ vcdchk |
/eco32/trunk/tools/Makefile
4,7 → 4,7
|
BUILD = ../build |
|
DIRS = bin2dat bin2exo bin2mcs bit2exo bit2mcs chrgen dspmem |
DIRS = bin2dat bin2exo bin2mcs bit2exo bit2mcs chrgen dspmem vcdchk |
|
.PHONY: all install clean |
|