vcddiff/vcddiff.c

1460 lines
47 KiB
C

/* Copyright (c) 1991-2004 Pragmatic C Software Corp. */
/*
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 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.
*/
#define VERS2 "0.04c-veripool"
#define OFDT "07/28/2004"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#include "vcddiff.h"
static int get_token(FILE*, char*);
static void read_to_end(FILE*);
static char timescale(FILE*, int*);
static void add_signal(char*, char*, unsigned int, int, int);
static int get_var_type(char*);
static void variable(FILE*, char*);
static void alloc_sig_mem(void);
static int get_vkeywrd(char*);
static long get_lines(FILE*, int*, int*, char*);
static void print_signal_info(struct signal_t*, struct signal_t*, vtime_t, vtime_t, bool_t);
static void print_scalar_state(struct signal_t*, struct signal_t*, vtime_t, vtime_t, char, char,
bool_t);
static void print_vector_state(struct signal_t*, struct signal_t*, vtime_t, vtime_t, char*, char*,
bool_t);
static void print_scalar_edge(struct signal_t*, struct signal_t*, vtime_t, vtime_t, char, char,
bool_t);
static void print_edges(struct signal_t*, char*, int, int, char);
static void print_vector_edge(struct signal_t*, struct signal_t*, vtime_t, vtime_t, char*, char*,
bool_t);
static int get_nxt_chg(FILE*, char*, int*, int*, char*, vtime_t*, bool_t);
static vtime_t get_time_diffs(FILE*, FILE*, char*, char*, long, long, vtime_t, vtime_t, bool_t);
static void print_map(void);
static void compare_types(char*, char*, struct signal_t*, struct signal_t*);
static bool_t map(char*, char*, int*, struct signal_t*, struct signal_t*, struct signal_t**,
bool_t);
static bool_t map_var_names(char*, char*);
static void run_diffs(FILE*, FILE*, char*, char*, long, long);
static void print_header(void);
static void print_help(void);
static void edge_space(int, int, bool_t);
/* globals */
static int line_num1G; /* cur position of file1 */
static int line_num2G; /* cur position of file2 */
static char curmodG[1000]; /* cur mod hier name */
static FILE* file1G; /* to check if it is the first file */
static bool_t state_flagG; /* print edges or states */
static bool_t wrap_flagG; /* print edges or states */
static bool_t extended_flagG; /* parse extended VCD format */
static struct signal_t** sig_int1G; /* int codes for file 1 */
static struct signal_t** sig_int2G; /* int codes for file 2 */
static int* fd1_to_fd2_mapG; /* mappings from one file to the other*/
static int* fd2_to_fd1_mapG;
static int max_codeG; /* max code so sig_int? is large enough */
static char scopesG[MAXSCOPES][MAXSIG]; /* scope of mods */
static int quit_flagG; /* flag to quit */
static bool_t next_listG; /* to signal the next list of signals */
struct signal_t* sig1_hdG; /* the head of the first file of signals */
struct signal_t* sig2_hdG; /* the head of the second file of signals */
struct signal_t* lastsigG; /* mark the last signal of the file */
/* signal to code from dinotrace wave viewer www.veripool.org/dinotrace */
/*extended vcd converted, removes '<' */
#define VERILOG_ID_TO_POS(_code_) \
strpbrk(_code_, "<") && extended_flagG \
? atoi(_code_ + 1) \
: (_code_[0] \
? ((_code_[0] - 32) \
+ 94 \
* (_code_[1] \
? ((_code_[1] - 32) \
+ 94 \
* (_code_[2] ? ((_code_[2] - 32) \
+ 94 * (_code_[3] ? ((_code_[3] - 32)) : 0)) \
: 0)) \
: 0)) \
: 0)
#define VERILOG_POS_TO_SIG1(_pos_) \
(((unsigned)(_pos_) < (unsigned)max_codeG) ? sig_int1G[(_pos_)] : 0)
#define VERILOG_POS_TO_SIG2(_pos_) \
(((unsigned)(_pos_) < (unsigned)max_codeG) ? sig_int2G[(_pos_)] : 0)
/**********************************************************************/
/*get a new token from the file fp, and place in token, return the length*/
static int get_token(FILE* fp, char* token) {
int i, c;
i = 0;
while ((c = fgetc(fp)) != EOF) {
if (isspace(c)) {
if (c == '\n') {
if (fp == file1G)
line_num1G++;
else
line_num2G++;
}
continue;
}
break;
}
if (c == EOF) return EOF;
token[i++] = c;
while (!isspace(c = fgetc(fp)) && i < MAXTOKSIZE) {
if (c == EOF)
return EOF;
else
token[i++] = c;
}
if (i >= MAXTOKSIZE - 1) {
printf("Token too long.\n");
exit(1);
}
if (c == '\n') {
if (fp == file1G)
line_num1G++;
else
line_num2G++;
}
token[i] = '\0';
return i;
}
/*read the file until an $end is reached*/
static void read_to_end(FILE* fp) {
static char token[MAXTOKSIZE];
while (get_token(fp, token) != EOF) {
if (!strncmp(token, "$end", 4)) return;
}
}
/* get the timescale information for comparison return char of units, and
* the number 1, 10, 100 in tnum
* */
static char timescale(FILE* fp, int* tnum) {
int i, toklen;
static char
token[MAXTOKSIZE * 2]; //Needs to be able to hold 2 tokens because of the concatenation.
static char tmp[MAXTOKSIZE];
char* tok;
toklen = get_token(fp, token);
if (toklen == EOF) {
printf("*** ERROR Invalid timescale specification.\n");
exit(1);
}
assert(toklen < MAXTOKSIZE);
tok = token;
/* AIV 02/04/03 there can be a space between 1 ns, not always 1ns */
for (i = 0; i < toklen; i++) {
if (!isdigit(token[i])) break;
}
if (i == toklen) {
get_token(fp, tmp);
strcat(token, tmp);
}
*tnum = atoi(tok);
if (*tnum != 1 && *tnum != 10 && *tnum != 100) {
printf("*** ERROR-time number(%d) in timescale on line %d is illegal - assuming 1\n", *tnum,
(fp == file1G) ? line_num1G : line_num2G);
*tnum = 1;
}
while (isdigit(*tok)) tok++;
if (!*tok) get_token(fp, token);
switch (*tok) {
case 's':
case 'm':
case 'u':
case 'n':
case 'p':
case 'f': return (*tok);
default:
printf("*** ERROR-illegal character(%c) in timescale on line %d\n", *tok,
(fp == file1G) ? line_num1G : line_num2G);
return (0);
}
}
/* add a new signal to the list, init values signal name, identifies
* the code to index the array of signals, and the number of bits, if
* bits is 1 it is scalar
* */
static void add_signal(char* signame, char* ident, unsigned int code, int bits, int type) {
char* cp;
struct signal_t* newsig;
/*get rid of scapes before and after*/
while (isspace(*signame)) signame++;
for (cp = signame + strlen(signame) - 1; cp >= signame && isspace(*cp); cp--) *cp = '\0';
newsig = (struct signal_t*)malloc(sizeof(struct signal_t));
newsig->sig_code = code;
newsig->ident = (char*)malloc(strlen(ident) + 1);
/* init values to x's */
newsig->state = '?';
newsig->type = type;
newsig->in_both = TRUE;
newsig->size = bits;
newsig->next = NULL;
if (type == REAL) {
/*FIXME what about large reals ???*/
newsig->vector = (char*)malloc(REALSIZE);
newsig->vector[0] = '0';
newsig->vector[1] = '\0';
} else if (bits > 1) {
newsig->vector = (char*)malloc(bits + 2);
newsig->vector[bits] = '\0';
bits--;
for (; bits >= 0; bits--) newsig->vector[bits] = '?';
} else {
newsig->vector = NULL;
}
/*signal not found so print first diff*/
newsig->found = FALSE;
strcpy(newsig->ident, ident);
/*link signals for hash setup and mapping*/
if (lastsigG) lastsigG->next = newsig;
if (sig1_hdG == NULL) sig1_hdG = newsig;
if (next_listG && sig2_hdG == NULL) sig2_hdG = newsig;
lastsigG = newsig;
newsig->signame = (char*)malloc(strlen(signame) + 1);
strcpy(newsig->signame, signame);
max_codeG = MAX((unsigned)max_codeG, code + 1);
}
/*dump variable types must be kept in alphabetical order */
/*Values must match the var type #defines in vcddiff.h */
static struct variable_types_t var_types[]
= {{"bit", BIT}, // GTKWave's fst2vcd
{"byte", BYTE}, // GTKWave's fst2vcd
{"event", EVENT},
{"int", INT}, // GTKWave's fst2vcd
{"integer", INTEGER},
{"logic", LOGIC}, // GTKWave's fst2vcd
{"longint", LONGINT}, // GTKWave's fst2vcd
{"parameter", PARAMETER},
{"port", PORT}, // GTKWave's fst2vcd
{"real", REAL},
{"real_parameter", REAL}, // GTKWave's fst2vcd, handle as if real
{"realtime", REALTIME}, // GTKWave's fst2vcd
{"reg", REG},
{"shortint", SHORTINT}, // GTKWave's fst2vcd
{"supply0", SUPPLY0},
{"supply1", SUPPLY1},
{"time", TIME},
{"tri", TRI},
{"tri0", TRI0},
{"tri1", TRI1},
{"triand", TRIAND},
{"trior", TRIOR},
{"trireg", TRIREG},
{"wand", WAND},
{"wire", WIRE},
{"wor", WOR}};
#define VARTYPE (sizeof(var_types) / sizeof(struct variable_types_t))
static int get_var_type(char* tstr) {
int l, h;
int m, cv;
l = 0;
h = VARTYPE - 1;
for (;;) {
m = (l + h) / 2;
if ((cv = strcmp(var_types[m].vnam, tstr)) == 0) return (var_types[m].vnum);
if (cv < 0)
l = m + 1;
else
h = m - 1;
if (h < l) break;
}
return (-1);
}
/*var line so add a new signal with its identifier*/
static void variable(FILE* fp, char* file_name) {
char signame[MAXSIG];
char ident[MAXIDLENGTH];
static char token[MAXTOKSIZE];
int bits;
char type;
/*type, size*/
{
int len = get_token(fp, token);
if (len == EOF) {
printf("ERROR - EOF during declaration of variable. Aborting.\n");
exit(1);
}
}
/*most ofter a reg otherwise do a search */
if (strncmp(token, "reg", 3) == 0)
type = REG;
else
type = get_var_type(token);
if (type < 0 || type > UNDEFINED) {
type = UNDEFINED;
printf("Error- Unknown variable type %s\n", token);
}
/*according to the std ieee verilog
if it is port is an evcd (port not allowed in vcd for signal declaration)
*/
if (extended_flagG == FALSE && strncmp(token, "port", 4) == 0) {
extended_flagG = TRUE;
} else if (extended_flagG && strncmp(token, "port", 4) != 0) {
/*parsing an evcd file but encountered in a non standard definition*/
printf("Error - Unknown variable type for EVCD standard %s\n", token);
}
/* get number of bits */
/* AIV FIXME error recovery should be used here for invalid tokens
* if a $var line is short vars (starts with a '$') rewind, print
* message and return */
{
int len = get_token(fp, token);
if (len == EOF) {
printf("ERROR - EOF during declaration of variable. Aborting.\n");
exit(1);
}
}
bits = atoi(token);
if (bits < 0) {
printf("Negative size illegal on line %d of %s\n", (fp == file1G) ? line_num1G : line_num2G,
file_name);
return;
}
/*identifier*/
get_token(fp, token);
bzero(
ident,
sizeof(ident)); //ensure that ident is null-terminated, even it is too long to fit in ident
strncpy(ident, token, sizeof(ident) - 1);
for (char* s = token; *s; s++) {
if (!isprint(*s)) {
printf("*** ERROR-illegal character(%c) in signal name on line %d\n", *s,
(fp == file1G) ? line_num1G : line_num2G);
exit(1);
}
}
/*variable name*/
get_token(fp, token);
bzero(signame, MAXSIG);
strncpy(signame, curmodG, MAXSIG - 1);
{
int available = MAXSIG - 1 - strlen(signame);
if (available > 0) { strncat(signame, token, available); }
}
get_token(fp, token);
/*if there is a space between the var name and the range concat it
* to the name */
if (token[0] != '$') {
int available = MAXSIG - 1 - strlen(signame);
if (available > 0) { strncat(signame, token, available); }
}
add_signal(signame, ident, VERILOG_ID_TO_POS(ident), bits, type);
}
/* setup memory for the hash of signals, which is the max of all signals,
* then setup up arrays according to the signal's code index
*/
static void alloc_sig_mem(void) {
struct signal_t* sig;
sig_int1G = (struct signal_t**)calloc(sizeof(struct signal_t*), (max_codeG + 1));
for (sig = sig1_hdG; sig != NULL; sig = sig->next) sig_int1G[sig->sig_code] = sig;
sig_int2G = (struct signal_t**)calloc(sizeof(struct signal_t*), (max_codeG + 1));
for (sig = sig2_hdG; sig != NULL; sig = sig->next) sig_int2G[sig->sig_code] = sig;
}
/*dump file keywords must be kept in alphabetical order */
static struct variable_types_t vkeywds[]
= {{"attrbegin", V_ATTRBEGIN}, // GTKWave
{"comment", V_COMMENT}, {"date", V_DATE}, {"end", V_END},
{"enddefinitions", V_ENDDEF}, {"scope", V_SCOPE}, {"timescale", V_TIMESCALE},
{"upscope", V_UPSCOPE}, {"var", V_VAR}, {"version", V_VERSION}};
#define VKEYWDS (sizeof(vkeywds) / sizeof(struct variable_types_t))
/*search for verilog keywords*/
static int get_vkeywrd(char* tstr) {
int l, h;
int m, cv;
//start at $end
l = -2;
h = VKEYWDS - 1;
for (;;) {
m = (l + h) / 2;
if (m < 0) return EOF;
if ((cv = strcmp(vkeywds[m].vnam, tstr)) == 0) return (vkeywds[m].vnum);
if (cv < 0)
l = m + 1;
else
h = m - 1;
if (h < l) break;
}
return (EOF);
}
/* process all the lines until $enddef is reached, determines all variable
* names, seperated by a '.' between scopes, place the timescale number
* and units, and return the file location to start processing diffs
*/
static long get_lines(FILE* fp, int* units, int* tnum, char* file_name) {
char sep[2];
int level;
int i;
char* tok;
//1+because of the line with "tok++", which would otherwise make the buffer
//smaller than expected by get_token
static char token[MAXTOKSIZE + 1];
sep[0] = '.';
sep[1] = '\0';
level = 0;
*units = 1;
*tnum = 1;
while (get_token(fp, token) != EOF) {
tok = token;
if (tok[0] != '$') {
printf("***ERROR Unknown token '%s' on line %d of %s\n", tok,
(fp == file1G) ? line_num1G : line_num2G, file_name);
continue;
}
tok++;
switch (get_vkeywrd(tok)) {
case V_END: break;
case V_VAR: variable(fp, file_name); break;
case V_UPSCOPE:
if (level > 0) level--;
break;
case V_SCOPE:
get_token(fp, tok);
get_token(fp, tok);
if (level < MAXSCOPES) {
strcpy(scopesG[level], tok);
strcpy(curmodG, scopesG[0]);
strcat(curmodG, sep);
level++;
if (level) {
for (i = 1; i < level; i++) {
strcat(curmodG, scopesG[i]);
strcat(curmodG, sep);
}
}
} else {
printf("*** ERROR-exceeded max scope levels %d\n", level);
exit(0);
}
break;
case V_ATTRBEGIN:
case V_COMMENT:
case V_DATE:
case V_VERSION: read_to_end(fp); break;
case V_TIMESCALE: *units = timescale(fp, tnum); break;
case V_ENDDEF:
next_listG = TRUE;
lastsigG = NULL;
return (ftell(fp));
default:
printf("Unknown command '%s' on line %d of %s\n", tok,
(fp == file1G) ? line_num1G : line_num2G, file_name);
break;
}
}
return (-1);
}
/* print the diff signal information */
static void print_signal_info(struct signal_t* sig1, struct signal_t* sig2, vtime_t time1,
vtime_t time2, bool_t isone) {
if (time1 == time2) {
if (sig1->sig_code == sig2->sig_code)
printf("%s (%s) differs at time %lld\n", sig1->signame, sig1->ident, time1);
else
printf("%s (%s , %s) differs at time %lld\n", sig1->signame, sig1->ident, sig2->ident,
time1);
return;
} else {
if (sig1->sig_code == sig2->sig_code)
printf("%c %s (%s) at time %lld next occurence at time %lld\n", isone ? '>' : '<',
sig1->signame, sig1->ident, time1, time2);
else
printf("%c %s (%s, %s) at time %lld next occurence at time %lld\n", isone ? '>' : '<',
sig1->signame, sig1->ident, sig2->ident, time1, time2);
return;
}
}
/* print the scalar value */
static void print_scalar_state(struct signal_t* sig1, struct signal_t* sig2, vtime_t time1,
vtime_t time2, char state1, char state2, bool_t isone) {
if (time1 == time2) {
sig1->found = TRUE;
sig2->found = TRUE;
if (state1 != state2) {
print_signal_info(sig1, sig2, time1, time2, isone);
printf("\t%c\n\t%c\n", state1, state2);
return;
}
} else {
print_signal_info(sig1, sig2, time1, time2, isone);
printf("%c #%lld \t%c\n", isone ? '>' : '<', time1, state1);
printf("%c #%lld \t%c\n", isone ? '>' : '<', time2, state2);
return;
}
}
/* print the vector value */
static void print_vector_state(struct signal_t* sig1, struct signal_t* sig2, vtime_t time1,
vtime_t time2, char* sval1, char* sval2, bool_t isone) {
int size1, size2;
int i;
size1 = strlen(sval1);
size2 = strlen(sval2);
if (time1 == time2) {
sig1->found = TRUE;
sig2->found = TRUE;
if (strcmp(sval1, sval2) != 0) {
print_signal_info(sig1, sig2, time1, time2, isone);
if (size1 == size2)
printf("\t%s\n\t%s\n", sval1, sval2);
else {
if (size1 > size2) {
printf("\t%s\n\t", sval1);
for (i = 0; i < size1 - size2; i++) putc(' ', stdout);
printf("%s\n", sval2);
} else {
printf("\t");
for (i = 0; i < size2 - size1; i++) putc(' ', stdout);
printf("%s\n\t%s\n", sval1, sval2);
}
}
return;
}
} else {
print_signal_info(sig1, sig2, time1, time2, isone);
if (size1 == size2) {
printf("%c #%lld \t%s\n", isone ? '>' : '<', time1, sval1);
printf("%c #%lld \t%s\n", isone ? '>' : '<', time2, sval2);
} else {
if (size1 > size2) {
printf("%c #%lld \t%s\n", isone ? '>' : '<', time1, sval1);
printf("%c #%lld \t", isone ? '>' : '<', time2);
for (i = 0; i < size1 - size2; i++) putc(' ', stdout);
printf("%s\n", sval2);
} else {
printf("%c #%lld \t", isone ? '>' : '<', time1);
for (i = 0; i < size2 - size1; i++) putc(' ', stdout);
printf("%s\n", sval1);
printf("%c #%lld \t%s\n", isone ? '>' : '<', time2, sval2);
}
}
}
}
/* print scalar edge value */
static void print_scalar_edge(struct signal_t* sig1, struct signal_t* sig2, vtime_t time1,
vtime_t time2, char state1, char state2, bool_t isone) {
if (time1 == time2) {
sig1->found = TRUE;
sig2->found = TRUE;
if (state1 != state2) {
print_signal_info(sig1, sig2, time1, time2, isone);
if (sig1->state == state1)
printf("\t(%c-)\n", state1);
else
printf("\t(%c%c)\n", sig1->state, state1);
if (sig2->state == state2)
printf("\t(%c-)\n", state2);
else
printf("\t(%c%c)\n", sig2->state, state2);
return;
}
} else {
print_signal_info(sig1, sig2, time1, time2, isone);
if (sig1->state == state1)
printf("%c #%lld \t(%c-)\n", isone ? '>' : '<', time1, state1);
else
printf("%c #%lld \t(%c%c)\n", isone ? '>' : '<', time1, sig1->state, state1);
if (sig2->state == state2)
printf("%c #%lld \t(%c-)\n", isone ? '>' : '<', time2, state2);
else
printf("%c #%lld \t(%c%c)\n", isone ? '>' : '<', time2, sig2->state, state2);
return;
}
}
/* print the edge values
sig - signal pointer
sval - sval string value
str_size - size of sval
search - '<' or '>' if a search was performed otherwise 'n' for none
this is needed for wrapping of the line
*/
static void print_edges(struct signal_t* sig, char* sval, int str_size, int vec_size,
char searchc) {
int i;
int min, tprint;
/*AIV 02/06/03 the print edges were wrong for different sizes */
/* for example, b10 is now b101 at the next time */
/* vector is the old value and sval is the new */
if (str_size > vec_size)
min = vec_size;
else
min = str_size;
tprint = 0;
for (i = 0; i < min; i++, tprint++) {
if (sig->vector[i] == sval[i])
printf(" (%c-)", sig->vector[i]);
else
printf(" (%c%c)", sig->vector[i], sval[i]);
/* only want to print 11 edges per line */
if (wrap_flagG && tprint != 0 && !(tprint % EDGE_PER_LINE)) {
if (searchc != 'n')
printf(" \\\n%c\t", searchc);
else
printf(" \\\n\t");
}
}
if (str_size == vec_size) return;
if (min == vec_size) {
for (i = min; i < str_size; i++, tprint++) {
printf(" (?%c)", sval[i]);
if (wrap_flagG && tprint != 0 && !(tprint % EDGE_PER_LINE)) {
if (searchc != 'n')
printf(" \\\n%c\t", searchc);
else
printf(" \\\n\t");
}
}
} else {
/* if the first value is a '?', no need to print the rest
* since it can only be '?' on the first value change */
if (sig->vector[0] != '?') {
for (i = min; i < vec_size; i++, tprint++) {
printf(" (%c?)", sig->vector[i]);
if (wrap_flagG && tprint != 0 && !(tprint % EDGE_PER_LINE)) {
if (searchc != 'n')
printf(" \\\n%c\t", searchc);
else
printf(" \\\n\t");
}
}
}
}
}
/* add space so the vector edge valued line up correctly */
static void edge_space(int size1, int size2, bool_t is_sigone) {
int i, r, diff;
if (size1 == size2) return;
if (is_sigone && size2 > size1) {
diff = (size2 - size1) * CHAR_PER_EDGE;
if (wrap_flagG) {
r = diff / EDGE_PER_LINE;
if (r > 0) {
r = (r * EDGE_PER_LINE);
diff -= r;
if (diff <= CHAR_PER_EDGE) diff = 0;
}
}
for (i = 0; i < diff; i++) putc(' ', stdout);
}
if (!is_sigone && size1 > size2) {
diff = (size1 - size2) * CHAR_PER_EDGE;
if (wrap_flagG) {
r = diff / EDGE_PER_LINE;
if (r > 0) {
r = (r * EDGE_PER_LINE);
diff -= r;
if (diff <= CHAR_PER_EDGE) diff = 0;
}
}
for (i = 0; i < diff; i++) putc(' ', stdout);
}
}
/* print vector edge */
static void print_vector_edge(struct signal_t* sig1, struct signal_t* sig2, vtime_t time1,
vtime_t time2, char* sval1, char* sval2, bool_t isone) {
int size1, size2;
int vsize1, vsize2;
int max1, max2;
if (time1 == time2) {
sig1->found = TRUE;
sig2->found = TRUE;
if (strcmp(sval1, sval2) != 0) {
if (sig1->type == REAL) {
print_vector_state(sig1, sig2, time1, time2, sval1, sval2, isone);
return;
}
print_signal_info(sig1, sig2, time1, time2, isone);
printf("\t");
size1 = strlen(sval1);
vsize1 = sig1->vector ? strlen(sig1->vector) : 0;
max1 = MAX(size1, vsize1);
size2 = strlen(sval2);
vsize2 = sig2->vector ? strlen(sig2->vector) : 0;
max2 = MAX(size2, vsize2);
edge_space(max1, max2, TRUE);
print_edges(sig1, sval1, size1, vsize1, 'n');
if (wrap_flagG && sig1->size > EDGE_PER_LINE)
printf("\n\n\t");
else
printf("\n\t");
edge_space(max1, max2, FALSE);
print_edges(sig2, sval2, size2, vsize2, 'n');
printf("\n");
return;
}
} else {
char dirc;
if (sig1->type == REAL) {
print_vector_state(sig1, sig2, time1, time2, sval1, sval2, isone);
return;
}
print_signal_info(sig1, sig2, time1, time2, isone);
dirc = isone ? '>' : '<';
printf("%c #%lld \t", dirc, time1);
size1 = strlen(sval1);
vsize1 = sig1->vector ? strlen(sig1->vector) : 0;
max1 = MAX(size1, vsize1);
size2 = strlen(sval2);
vsize2 = sig2->vector ? strlen(sig2->vector) : 0;
max2 = MAX(size2, vsize2);
edge_space(max1, max2, TRUE);
print_edges(sig1, sval1, size1, vsize1, dirc);
printf("\n%c #%lld \t", dirc, time2);
edge_space(max1, max2, FALSE);
print_edges(sig2, sval2, size2, vsize2, dirc);
printf("\n");
return;
}
}
/* get the next time change in the other file if there is one */
static int get_nxt_chg(FILE* fp, char* fname, int* sigcode, int* bit, char* value, vtime_t* time,
bool_t isone) {
char* line;
char* separator;
int token_length;
static char token[MAXTOKSIZE];
while (get_token(fp, token) != EOF) {
line = token;
switch (*line++) {
/* scalar cases */
case '0':
case '1':
case 'z':
case 'Z':
case 'x':
case 'X':
if (extended_flagG) {
/*check if it contains '<', if true parsing the eVCD*/
separator = strpbrk(token, "<"); // get the separator
if (!separator) {
printf("Unknown Identifier not found '%s' in file %d '%s' on line %d\n", line,
isone ? 1 : 2, fname, isone ? line_num1G : line_num2G);
continue;
} else if (separator - line > 1) {
/*it is a vector*/
line--;
get_token(fp, token);
strncpy(value, line, separator - line);
*sigcode = VERILOG_ID_TO_POS(separator);
if (isone) {
if (VERILOG_POS_TO_SIG1(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line,
isone ? 1 : 2, fname, isone ? line_num1G : line_num2G);
continue;
}
} else if (VERILOG_POS_TO_SIG2(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line,
isone ? 1 : 2, fname, isone ? line_num1G : line_num2G);
continue;
}
return (VECTOR);
}
}
/*otherwise vcd*/
*bit = *(line - 1);
*sigcode = VERILOG_ID_TO_POS(line);
if (isone) {
if (VERILOG_POS_TO_SIG1(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line, isone ? 1 : 2,
fname, isone ? line_num1G : line_num2G);
continue;
}
} else if (VERILOG_POS_TO_SIG2(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line, isone ? 1 : 2,
fname, isone ? line_num1G : line_num2G);
continue;
}
return (SCALAR);
break;
/* vector or real cases */
case 'b':
case 'r':
strncpy(value, line, MAXTOKSIZE);
get_token(fp, token);
*sigcode = VERILOG_ID_TO_POS(token);
if (isone) {
if (VERILOG_POS_TO_SIG1(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line, isone ? 1 : 2,
fname, isone ? line_num1G : line_num2G);
continue;
}
} else if (VERILOG_POS_TO_SIG2(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line, isone ? 1 : 2,
fname, isone ? line_num1G : line_num2G);
continue;
}
return (VECTOR);
case 'p':
/*pport_value 0_strength_component 1_strength_component identifier_code*/
/*parsing "port_value"*/
strncpy(value, line, MAXTOKSIZE);
/*parsing "0_strength_component"*/
token_length = get_token(fp, token);
strncat(value, " ", 2); /* separate port value from strength */
strncat(value, token, token_length);
/*parsing "1_strength_component"*/
token_length = get_token(fp, token);
strncat(value, " ", 2); /* separate port value from strength */
strncat(value, token, token_length);
get_token(fp, token);
/*parsing "identifier_code"*/
*sigcode = VERILOG_ID_TO_POS(token);
if (isone) {
if (VERILOG_POS_TO_SIG1(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line, isone ? 1 : 2,
fname, isone ? line_num1G : line_num2G);
continue;
}
} else if (VERILOG_POS_TO_SIG2(*sigcode) == NULL) {
printf("Unknown Identifier '%s' in file %d '%s' on line %d\n", line, isone ? 1 : 2,
fname, isone ? line_num1G : line_num2G);
continue;
}
return (VECTOR); /*fake vector if single bit*/
/* time change value */
case '#': *time = (vtime_t)atoll(line); return (TIME);
case '$':
/* AIV 02/03/03 need to read until $end for $comment */
if (!strcmp(line, "comment") || !strcmp(line, "dumpall")) read_to_end(fp);
if (!strcmp(line, "dumpports") && extended_flagG)
get_token(fp, token); /*skip dumpports keyword*/
break;
default:
line--;
printf("***ERROR Unknown token '%s' in file %d '%s' on line %d\n", line, isone ? 1 : 2,
fname, isone ? line_num1G : line_num2G);
/*FIXME shouldn't die here if possible */
exit(0);
continue;
break;
}
}
return (EOF);
}
/* used to place the string back on the fp to be used later */
static void restore(FILE* fp, char* str, int size) {
int i;
char* cp;
{
int r = ungetc(' ', fp);
assert(r != EOF);
}
cp = &str[size - 1];
for (i = 0; i < size; i++, cp--) {
int r = ungetc(*cp, fp);
if (r == EOF) {
//because ungetc implementations are not required to support
//more than one call of it in a row, this sort of usage is
//fundamentally flawed.
printf("*** ERROR Failed using ungetc. Aborting.\n");
exit(1);
}
}
}
/* return true if the next signal isn't the same, otherwise false */
static bool_t peek_nxt_sig(FILE* fp, int sigcode1, bool_t isone) {
static char tmp[MAXTOKSIZE], sig[MAXTOKSIZE];
int size1, sigcode2;
char* cp;
size1 = get_token(fp, tmp);
if (size1 == EOF) return (TRUE);
cp = tmp;
if (tmp[0] == 'b') {
int size2;
size2 = get_token(fp, sig);
restore(fp, sig, size2);
restore(fp, tmp, size1);
cp = sig;
} else if (tmp[0] == '0' || tmp[0] == '1' || tmp[0] == 'x' || tmp[0] == 'z') {
restore(fp, tmp, size1);
cp++;
} else {
restore(fp, tmp, size1);
return (TRUE);
}
/* map signal to the right sigcode */
sigcode2 = VERILOG_ID_TO_POS(cp);
/* LOOKATME can this ever happen ??*/
if (sigcode2 < 0 || sigcode2 > max_codeG) return (TRUE);
if (isone) {
if (fd2_to_fd1_mapG[sigcode2] == -1) return (TRUE);
sigcode2 = fd2_to_fd1_mapG[sigcode2];
} else {
if (fd1_to_fd2_mapG[sigcode2] == -1) return (TRUE);
sigcode2 = fd1_to_fd2_mapG[sigcode2];
}
return (sigcode1 != sigcode2);
}
/* rewind to file and reset line count to the start of a #time */
static void rewind_time(FILE* fp, long seek, vtime_t* time, vtime_t treset, int* linenum,
int lreset) {
*time = treset;
fseek(fp, seek, SEEK_SET);
/* reset the line counts */
*linenum = lreset;
}
/* get the differences of a time block from #time to the next #time2 or EOF */
static vtime_t get_time_diffs(FILE* mainfp, FILE* otherfp, char* mname, char* oname, long seek1,
long seek2, vtime_t ltime1, vtime_t ltime2, bool_t isone) {
static char svalue1[MAXTOKSIZE], svalue2[MAXTOKSIZE];
int retval = 0;
int sigcode1, sigcode2;
int state1 = '?', state2 = '?';
int tmpline1, tmpline2;
vtime_t time, nxt_time;
struct signal_t *sig1, *sig2;
bool_t first;
sigcode1 = sigcode2 = 0;
first = TRUE;
time = ltime1;
nxt_time = ltime2;
/* AIV 02/04/03 added temps to reset line count for unkown tokens, etc */
tmpline1 = line_num1G;
tmpline2 = line_num2G;
/* LOOKATME rewind needed here because of reset ungets, and seek from
the start of the file, should write own buffer */
rewind(isone ? mainfp : otherfp);
if (isone)
fseek(mainfp, seek1, SEEK_CUR);
else
fseek(otherfp, seek2, SEEK_CUR);
while (retval != EOF) {
retval = get_nxt_chg(mainfp, mname, &sigcode1, &state1, svalue1, &time, isone);
switch (retval) {
case SCALAR:
case VECTOR:
/* rewind if the next sig isn't the same */
/* LOOKATME could change so that if the first sig isn't*/
/* get_nxt would rewind, to speed up ?? */
if (!first && peek_nxt_sig(otherfp, sigcode1, isone)) {
rewind_time(otherfp, seek2, &nxt_time, ltime2, isone ? &line_num2G : &line_num1G,
isone ? tmpline2 : tmpline1);
}
first = FALSE;
if (isone) {
sig1 = VERILOG_POS_TO_SIG1(sigcode1);
sigcode1 = fd1_to_fd2_mapG[sigcode1];
sig2 = VERILOG_POS_TO_SIG2(sigcode1);
} else {
sig1 = VERILOG_POS_TO_SIG2(sigcode1);
sigcode1 = fd2_to_fd1_mapG[sigcode1];
sig2 = VERILOG_POS_TO_SIG1(sigcode1);
}
if (!sig1->in_both) break;
if (sig1->found) {
sig1->found = FALSE;
sig2->found = FALSE;
if (retval == SCALAR)
sig1->state = state1;
else {
if (sig1->vector) { strcpy(sig1->vector, svalue1); }
}
break;
}
/*should check to see if sig1 and sig 2 are the same type??*/
for (;;) {
if (get_nxt_chg(otherfp, oname, &sigcode2, &state2, svalue2, &nxt_time, !isone)
== EOF) {
printf("%s (%s) Never found in file %d at time %lld\n", sig1->signame, sig1->ident,
isone ? 1 : 2, time);
break;
} else if (sigcode1 == sigcode2)
break;
}
if (retval == SCALAR) {
if (state_flagG)
print_scalar_state(sig1, sig2, time, nxt_time, state1, state2, isone);
else
print_scalar_edge(sig1, sig2, time, nxt_time, state1, state2, isone);
sig1->state = state1;
} else if (retval == VECTOR) {
if (state_flagG)
print_vector_state(sig1, sig2, time, nxt_time, svalue1, svalue2, isone);
else
print_vector_edge(sig1, sig2, time, nxt_time, svalue1, svalue2, isone);
if (sig1->vector) { strcpy(sig1->vector, svalue1); }
}
/* if isn't the same time must of scanned foward so rewind */
if (nxt_time != time) {
rewind_time(otherfp, seek2, &nxt_time, ltime2, isone ? &line_num2G : &line_num1G,
isone ? tmpline2 : tmpline1);
}
break;
case TIME:
/* rewind to the start time, to process other file*/
if (isone) {
rewind_time(otherfp, seek2, &nxt_time, ltime2, isone ? &line_num2G : &line_num1G,
isone ? tmpline2 : tmpline1);
}
if (time != 0) return (time);
break;
case EOF: quit_flagG = EOF; return (time);
default: continue;
}
}
/*FIXME should never get here emit error */
return (time);
}
/* print the map of identifiers to signal name between files */
static void print_map(void) {
int i;
struct signal_t* sig;
for (i = 0; i < max_codeG; i++) {
if (fd1_to_fd2_mapG[i] != -1) {
sig = VERILOG_POS_TO_SIG2(fd1_to_fd2_mapG[i]);
if (!sig || !sig->in_both) continue;
printf("%d %d %s\n", i, fd1_to_fd2_mapG[i], sig->signame);
}
}
printf("*********Second*********\n");
for (i = 0; i < max_codeG; i++) {
if (fd2_to_fd1_mapG[i] != -1) {
sig = VERILOG_POS_TO_SIG1(fd2_to_fd1_mapG[i]);
if (!sig || !sig->in_both) continue;
printf("%d %d %s\n", i, fd2_to_fd1_mapG[i], sig->signame);
}
}
}
static void print_var_type_name(int vnum) {
if (vnum < 0 || vnum >= UNDEFINED) {
printf("UNDEFINED");
} else {
printf("'%s'", var_types[vnum].vnam);
}
}
/* check to make sure the files are the same in type/size in both files */
static void compare_types(char* file_nam1, char* file_nam2, struct signal_t* sig1,
struct signal_t* sig2) {
if (sig1->in_both) {
if (sig1->type != sig2->type) {
if (sig1->sig_code == sig2->sig_code)
printf("WARNING - Ignoring signal %s (%s) - types don't match\n", sig1->signame,
sig1->ident);
else
printf("WARNING - Ignoring signal %s (%s %s) - types don't match\n", sig1->signame,
sig1->ident, sig2->ident);
printf("\t file '%s' type ", file_nam1);
print_var_type_name(sig1->type);
printf("\n");
printf("\t file '%s' type ", file_nam2);
print_var_type_name(sig2->type);
printf("\n\n");
sig1->in_both = FALSE;
sig2->in_both = FALSE;
return;
}
if (sig1->size != sig2->size) {
if (sig1->sig_code == sig2->sig_code)
printf("WARNING - Ignoring signal %s (%s) - sizes don't match\n", sig1->signame,
sig1->ident);
else
printf("WARNING - Ignoring signal %s (%s %s) - sizes don't match\n", sig1->signame,
sig1->ident, sig2->ident);
printf("\t file '%s' size '%d'\n", file_nam1, sig1->size);
printf("\t file '%s' size '%d'\n\n", file_nam2, sig2->size);
sig1->in_both = FALSE;
sig2->in_both = FALSE;
return;
}
}
}
/* map the signal names to identifiers from one file to the other */
static bool_t map(char* file_nam1, char* file_nam2, int* file_map, struct signal_t* startsig,
struct signal_t* otherstart, struct signal_t** sig_arr, bool_t isone) {
struct signal_t *sig1, *sig2;
bool_t one_found;
if (sig_arr) {} // UNUSED
one_found = FALSE;
for (sig1 = startsig; sig1 != NULL; sig1 = sig1->next) {
if (isone)
sig2 = VERILOG_POS_TO_SIG2(sig1->sig_code);
else
sig2 = VERILOG_POS_TO_SIG1(sig1->sig_code);
if (sig2 == NULL) {
for (sig2 = otherstart; sig2 != NULL; sig2 = sig2->next) {
if (!sig2->in_both) continue;
if (strcmp(sig1->signame, sig2->signame) == 0) {
file_map[sig1->sig_code] = sig2->sig_code;
compare_types(file_nam1, file_nam2, sig1, sig2);
one_found = TRUE;
break;
}
}
} else if (strcmp(sig1->signame, sig2->signame) == 0) {
file_map[sig1->sig_code] = sig1->sig_code;
compare_types(file_nam1, file_nam2, sig1, sig2);
one_found = TRUE;
} else {
for (sig2 = otherstart; sig2 != NULL; sig2 = sig2->next) {
if (!sig2->in_both) continue;
if (strcmp(sig1->signame, sig2->signame) == 0) {
file_map[sig1->sig_code] = sig2->sig_code;
compare_types(file_nam1, file_nam2, sig1, sig2);
one_found = TRUE;
break;
}
}
}
if (sig2 == NULL) {
printf("WARNING - Ignoring signal %s (%s) - not defined in both files\n", sig1->signame,
sig1->ident);
printf("\t Defined in file '%s' and not file '%s'\n\n", file_nam1, file_nam2);
file_map[sig1->sig_code] = sig1->sig_code;
sig1->in_both = FALSE;
}
}
return (one_found);
}
/* map both files */
static bool_t map_var_names(char* file_nam1, char* file_nam2) {
fd1_to_fd2_mapG = (int*)malloc(sizeof(int) * max_codeG);
for (int i = 0; i < max_codeG; i++) fd1_to_fd2_mapG[i] = -1;
fd2_to_fd1_mapG = (int*)malloc(sizeof(int) * max_codeG);
for (int i = 0; i < max_codeG; i++) fd2_to_fd1_mapG[i] = -1;
if (0) { print_map(); }
map(file_nam1, file_nam2, fd1_to_fd2_mapG, sig1_hdG, sig2_hdG, sig_int1G, TRUE);
return (map(file_nam2, file_nam1, fd2_to_fd1_mapG, sig2_hdG, sig1_hdG, sig_int2G, FALSE));
}
/* acutally run the files to diff the two */
static void run_diffs(FILE* fp1, FILE* fp2, char* fnam1, char* fnam2, long start1, long start2) {
vtime_t time1, time2, temp_time1;
long temp1_chars;
static char token[MAXTOKSIZE];
time1 = time2 = temp_time1 = 0;
while (quit_flagG != EOF) {
/* while the times are the same */
while (time1 == time2) {
temp_time1 = time1;
time1 = get_time_diffs(fp1, fp2, fnam1, fnam2, start1, start2, time1, time2, TRUE);
temp1_chars = ftell(fp1);
time2 = get_time_diffs(fp2, fp1, fnam2, fnam1, start2, start1, time2, temp_time1, FALSE);
start2 = ftell(fp2);
start1 = temp1_chars;
if (quit_flagG) break;
}
/* time one is ahead */
while (time2 < time1) {
time2 = get_time_diffs(fp2, fp1, fnam2, fnam1, start2, start1, time2, temp_time1, FALSE);
start2 = ftell(fp2);
if (quit_flagG) {
if (get_token(fp1, token) != EOF) {
printf("*****End of file hit at file 2 ('%s') time %lld quiting\n", fnam2, time2);
printf("WARNING - Files have different end times\n");
}
break;
}
}
/* while time two is ahead */
while (time1 < time2) {
time1 = get_time_diffs(fp1, fp2, fnam1, fnam2, start1, start2, time1, time2, TRUE);
start1 = ftell(fp1);
if (quit_flagG) {
if (get_token(fp2, token) != EOF) {
printf("*****End of file hit at file 1 ('%s') time %lld quiting\n", fnam1, time1);
printf("WARNING - Files have different end times\n");
}
break;
}
}
}
}
/* print help information */
static void print_help(void) {
printf("Usage: [options] 'file1' 'file2'\n Compares Verilog VCD dump files.\n\n");
printf(" --state \n -s \n");
printf("\tPrints the current state of variables, instead of\n \tthe default edge value. \n");
printf(" --wrap \n -w \n");
printf("\tWraps the line, only used for default edge print values\n\tnot --state print "
"messages. \n");
printf("\n");
print_header();
exit(0);
}
/* print program header info */
static void print_header(void) {
char s1[30];
time_t now;
printf("%s_%s of %s (%s).\n", "vcddiff", VERS2, OFDT, "Linux-ELF");
printf("Copyright (c) 2002-2004 Pragmatic C Software Corp.\n");
printf("All Rights reserved. Licensed under the GNU General Public License (GPL).\n");
printf("See the 'COPYING' file for details. NO WARRANTY provided.\n");
time(&now);
strcpy(s1, ctime(&now));
s1[24] = '\0';
printf("Today is %s.\n\n", s1);
}
static void set_options(int argc, char** argv) {
int i;
for (i = 1; i < argc - 2; i++) {
if (!strcmp(argv[i], "--state") || !strcmp(argv[i], "-s")) {
if (wrap_flagG)
printf("WARNING - wrap cannot be used with --state option, wrap disabled\n\n");
else
state_flagG = TRUE;
} else if (!strcmp(argv[i], "--wrap") || !strcmp(argv[i], "-w")) {
if (state_flagG)
printf("WARNING - wrap cannot be used with --state option, wrap disabled\n\n");
else
wrap_flagG = TRUE;
} else {
printf("ERROR - Unknown option %s\n", argv[i]);
printf("\t Try 'vcddiff --help' for more infomation\n");
exit(0);
}
}
}
int main(int argc, char** argv) {
int unit1, unit2, tnum1, tnum2;
long start1, start2;
char *file_nam1, *file_nam2;
bool_t map_found;
FILE *fp1, *fp2;
extended_flagG = FALSE;
quit_flagG = FALSE;
sig1_hdG = NULL;
sig2_hdG = NULL;
//print_header();
if (argc < 2) {
printf("ERROR - Usage: [options] 'file1' 'file2'\n");
printf("\t Try 'vcddiff --help' for more infomation\n");
exit(1);
}
if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) print_help();
if (argc < 3) {
printf("ERROR - Usage: [options] 'file1' 'file2'\n");
printf("\t Try 'vcddiff --help' for more infomation\n");
exit(1);
}
wrap_flagG = state_flagG = FALSE;
if ((fp1 = fopen(argv[argc - 2], "r")) == NULL) {
printf("*** ERROR-opening file %s\n", argv[argc - 2]);
exit(1);
}
/* set first file to the global pointer to check the line count */
file1G = fp1;
if ((fp2 = fopen(argv[argc - 1], "r")) == NULL) {
printf("*** ERROR-opening file %s\n", argv[argc - 1]);
exit(1);
}
set_options(argc, argv);
sig_int1G = NULL;
sig_int2G = NULL;
/*could skip copying the filename, just pass argv*/
file_nam1 = (char*)malloc(strlen(argv[argc - 2]) + 1);
strcpy(file_nam1, argv[argc - 2]);
start1 = get_lines(fp1, &unit1, &tnum1, file_nam1);
file_nam2 = (char*)malloc(strlen(argv[argc - 1]) + 1);
strcpy(file_nam2, argv[argc - 1]);
start2 = get_lines(fp2, &unit2, &tnum2, file_nam2);
alloc_sig_mem();
map_found = map_var_names(file_nam1, file_nam2);
if (map_found) {
if (tnum1 != tnum2) {
printf("*** ERROR-dump files have different timescale time numbers '%d' in file 1, and "
"'%d' in file 2\n",
tnum1, tnum2);
exit(1);
}
if (unit1 != unit2) {
printf("*** ERROR-dump files have different time scale units '%cs' in file 1, and '%cs' "
"in file 2\n",
unit1, unit2);
exit(1);
}
run_diffs(fp1, fp2, file_nam1, file_nam2, start1, start2);
} else
printf("*** ERROR-dump files have no matching variables to diff\n");
/* free and close */
free(sig_int1G);
free(sig_int2G);
free(fd1_to_fd2_mapG);
free(fd2_to_fd1_mapG);
fclose(fp1);
fclose(fp2);
return (0);
}