-
Notifications
You must be signed in to change notification settings - Fork 2
/
diffvcd.py
executable file
·156 lines (134 loc) · 4.96 KB
/
diffvcd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python3
import argparse
import re
import sys
from vcdvcd import VCDVCD, binary_string_to_hex
from typing import *
parser = argparse.ArgumentParser(
description="Print the first difference between two VCD files")
parser.add_argument("file1", metavar="VCD1", help="first file to compare")
parser.add_argument("file2", metavar="VCD2", help="second file to compare")
parser.add_argument("--top1",
metavar="INSTPATH",
help="instance in first file to compare")
parser.add_argument("--top2",
metavar="INSTPATH",
help="instance in second file to compare")
parser.add_argument("-f",
"--filter",
metavar="REGEX",
action="append",
default=[],
help="only compare signals matching a regex")
parser.add_argument("-i",
"--ignore",
metavar="REGEX",
action="append",
default=[],
help="ignore signals matching a regex")
parser.add_argument("-l",
"--list",
action="store_true",
help="list signals and exit")
parser.add_argument("-v",
"--verbose",
action="store_true",
help="verbose output")
parser.add_argument("-a", "--after", type=int, help="only compare after time")
parser.add_argument("-b",
"--before",
type=int,
help="only compare before time")
args = parser.parse_args()
def info(s: str):
if args.verbose:
sys.stderr.write(s)
def infoln(s: str):
info(f"{s}\n")
# Collect the signals in both files.
vcd1 = VCDVCD(args.file1, only_sigs=True)
vcd2 = VCDVCD(args.file2, only_sigs=True)
infoln(f"{len(vcd1.signals)} signals in first file")
infoln(f"{len(vcd2.signals)} signals in second file")
# Extract signals under the requested top-level instance.
def filter_signals(signals: List[str],
prefix: Optional[str]) -> Dict[str, str]:
if prefix is None:
return dict((s, s) for s in signals)
else:
return dict(
(s[len(prefix):], s) for s in signals if s.startswith(prefix))
filtered_signals1 = filter_signals(vcd1.signals, args.top1)
filtered_signals2 = filter_signals(vcd2.signals, args.top2)
if args.top1 is not None:
infoln(
f"{len(filtered_signals1)} signals under `{args.top1}` in first file")
if args.top2 is not None:
infoln(
f"{len(filtered_signals2)} signals under `{args.top2}` in second file")
# Find the common signals.
common_signals = list()
for key, sig1 in filtered_signals1.items():
if sig2 := filtered_signals2.get(key):
common_signals.append((key, sig1, sig2))
common_signals.sort()
infoln(f"{len(common_signals)} common signals")
# Filter the common signals.
for f in args.filter:
f = re.compile(f)
common_signals = [x for x in common_signals if f.search(x[0])]
# Filter out ignored signals.
for i in args.ignore:
i = re.compile(i)
common_signals = [x for x in common_signals if not i.search(x[0])]
if args.filter or args.ignore:
infoln(f"{len(common_signals)} filtered and unignored signals")
# List the signals and exit if requested.
if args.list:
for s in common_signals:
print(s[0])
sys.exit(0)
# Abort if there are no common signals to compare.
if not common_signals:
sys.stderr.write("no commong signals between input files\n")
sys.exit(1)
# Read the VCD files with only the interesting signals.
infoln("Reading first file")
vcd1 = VCDVCD(args.file1, signals=[x[1] for x in common_signals])
infoln("Reading second file")
vcd2 = VCDVCD(args.file2, signals=[x[2] for x in common_signals])
# Compare each signal.
earliest_mismatches = []
for signal, signame1, signame2 in common_signals:
infoln(f"Comparing {signal}")
signal1 = vcd1[signame1]
signal2 = vcd2[signame2]
def skip_time(tv: List) -> List:
if args.after:
while tv and tv[0][0] < args.after:
tv = tv[1:]
return tv
endtime = max(signal1.endtime, signal2.endtime)
tv1 = skip_time(signal1.tv + [(endtime, signal1.tv[-1][1])])
tv2 = skip_time(signal2.tv + [(endtime, signal2.tv[-1][1])])
for (t1, v1), (t2, v2) in zip(tv1, tv2):
if t1 == t2 and v1 == v2:
continue
t = min(t1, t2)
if args.before and t > args.before:
break
v1 = signal1[t]
v2 = signal2[t]
if v1 == v2:
continue
if earliest_mismatches and t < earliest_mismatches[0][0]:
earliest_mismatches = []
if not earliest_mismatches or t == earliest_mismatches[0][0]:
earliest_mismatches.append((t, v1, v2, signal))
break
for t, sig1, sig2, name in earliest_mismatches:
print(
f"{t} {binary_string_to_hex(sig1)} {binary_string_to_hex(sig2)} {name}"
)
if earliest_mismatches:
sys.exit(1)