vcd2latex.py
· 3.5 KiB · Python
Bruto
#!/usr/bin/env python3
import vcdvcd, math
from vcdvcd import VCDVCD
vcdfile = "digital_port_tb.vcd"
vcd = VCDVCD(vcdfile, only_sigs=True)
all_signals = vcd.signals
# print(all_signals)
dataW = [0 for i in range(len(all_signals))]
data = [[] for i in range(len(all_signals))]
###########
def nibble_to_hex(s):
for c in s:
if not c in '01':
return c
return hex(int(s, 2))[2:].upper()
def binary_string_to_hex(s, w):
if len(s) == 1:
if s != '0' and s != '1':
return s
s = s.zfill(w)
n = 4
groups = [s[i:i+n] for i in range(0, len(s), n)]
hexgroups = [nibble_to_hex(x) for x in groups]
return "".join(hexgroups)
class ParserCallbacks(vcdvcd.StreamParserCallbacks):
def __init__(self, deltas=True):
self._deltas = deltas
self._references_to_widths = {}
def enddefinitions(
self,
vcd,
signals,
cur_sig_vals
):
if signals:
self._print_dumps_refs = signals
else:
self._print_dumps_refs = sorted(vcd.data[i].references[0] for i in cur_sig_vals.keys())
for i, ref in enumerate(self._print_dumps_refs, 1):
if i == 0:
i = 1
identifier_code = vcd.references_to_ids[ref]
size = int(vcd.data[identifier_code].size)
width = max(((size // 4)), int(math.floor(math.log10(i))) + 1)
self._references_to_widths[ref] = width
dataW[i-1] = size
def time(
self,
vcd,
time,
cur_sig_vals
):
if (not self._deltas or vcd.signal_changed):
ss = []
ss.append('{}'.format(time))
for i, ref in enumerate(self._print_dumps_refs):
identifier_code = vcd.references_to_ids[ref]
value = cur_sig_vals[identifier_code]
data[i].append(binary_string_to_hex(value, dataW[i]))
###########
callbacks = ParserCallbacks()
VCDVCD(
vcdfile,
signals=all_signals,
store_tvs=False,
callbacks=callbacks,
)
ltx = '''
\\documentclass{standalone}
\\usepackage{tikz-timing}
\\begin{document}
\\begin{tikztimingtable}[timing/xunit=35,timing/yunit=10]
'''
def latexEscape(val):
return val.\
replace("_", "\\_").\
replace("{", "\\{").\
replace("}", "\\}").\
replace("&", "\\&").\
replace("%", "\\%").\
replace("$", "\\$").\
replace("#", "\\#").\
replace("$", "\\$")
for sig in range(len(data)):
sigName = latexEscape(all_signals[sig])
sigSeries = data[sig]
sigWidth = dataW[sig]
# print(f"SigName: {sigName} - SigWidth: {sigWidth}")
ltx += format(f" {sigName} &")
for value in sigSeries:
prefix = ""
token = format(f" D{{{value}}} ")
if value == "x":
token = "X"
elif value == "z":
token = "Z"
elif sigWidth == 1:
token = "H" if value == "1" else "L"
if "x" in token:
prefix = " [red] "
elif "z" in token:
prefix = " [blue] "
ltx += format(f" {prefix}{token} ;")
ltx += " \\\\\n"
# ltx += "\\vertlines[help lines,opacity=0.3]{}\n"
ltx += '''\\extracode
\\vertlines[help lines,opacity=0.3]{}
\\end{tikztimingtable}
\\end{document}
'''
# print(ltx)
with open("tmp.tex", "w") as f:
f.write(ltx)
'''
\\begin{tikztimingtable}[timing/wscale=0.8]
M-cycle & X 8D{M1} 8D{M2/M1} X \\\\
Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{LD r, r'} ; [opacity=0.4] X \\\\
Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\\\
Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\\\
\\end{tikztimingtable}
'''
| 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import vcdvcd, math |
| 4 | from vcdvcd import VCDVCD |
| 5 | |
| 6 | vcdfile = "digital_port_tb.vcd" |
| 7 | |
| 8 | vcd = VCDVCD(vcdfile, only_sigs=True) |
| 9 | |
| 10 | all_signals = vcd.signals |
| 11 | # print(all_signals) |
| 12 | dataW = [0 for i in range(len(all_signals))] |
| 13 | data = [[] for i in range(len(all_signals))] |
| 14 | |
| 15 | ########### |
| 16 | |
| 17 | def nibble_to_hex(s): |
| 18 | for c in s: |
| 19 | if not c in '01': |
| 20 | return c |
| 21 | return hex(int(s, 2))[2:].upper() |
| 22 | |
| 23 | def binary_string_to_hex(s, w): |
| 24 | if len(s) == 1: |
| 25 | if s != '0' and s != '1': |
| 26 | return s |
| 27 | |
| 28 | s = s.zfill(w) |
| 29 | n = 4 |
| 30 | groups = [s[i:i+n] for i in range(0, len(s), n)] |
| 31 | hexgroups = [nibble_to_hex(x) for x in groups] |
| 32 | return "".join(hexgroups) |
| 33 | |
| 34 | |
| 35 | class ParserCallbacks(vcdvcd.StreamParserCallbacks): |
| 36 | def __init__(self, deltas=True): |
| 37 | self._deltas = deltas |
| 38 | self._references_to_widths = {} |
| 39 | |
| 40 | def enddefinitions( |
| 41 | self, |
| 42 | vcd, |
| 43 | signals, |
| 44 | cur_sig_vals |
| 45 | ): |
| 46 | if signals: |
| 47 | self._print_dumps_refs = signals |
| 48 | else: |
| 49 | self._print_dumps_refs = sorted(vcd.data[i].references[0] for i in cur_sig_vals.keys()) |
| 50 | for i, ref in enumerate(self._print_dumps_refs, 1): |
| 51 | if i == 0: |
| 52 | i = 1 |
| 53 | identifier_code = vcd.references_to_ids[ref] |
| 54 | size = int(vcd.data[identifier_code].size) |
| 55 | width = max(((size // 4)), int(math.floor(math.log10(i))) + 1) |
| 56 | self._references_to_widths[ref] = width |
| 57 | dataW[i-1] = size |
| 58 | |
| 59 | def time( |
| 60 | self, |
| 61 | vcd, |
| 62 | time, |
| 63 | cur_sig_vals |
| 64 | ): |
| 65 | if (not self._deltas or vcd.signal_changed): |
| 66 | ss = [] |
| 67 | ss.append('{}'.format(time)) |
| 68 | for i, ref in enumerate(self._print_dumps_refs): |
| 69 | identifier_code = vcd.references_to_ids[ref] |
| 70 | value = cur_sig_vals[identifier_code] |
| 71 | data[i].append(binary_string_to_hex(value, dataW[i])) |
| 72 | |
| 73 | |
| 74 | ########### |
| 75 | |
| 76 | |
| 77 | callbacks = ParserCallbacks() |
| 78 | VCDVCD( |
| 79 | vcdfile, |
| 80 | signals=all_signals, |
| 81 | store_tvs=False, |
| 82 | callbacks=callbacks, |
| 83 | ) |
| 84 | |
| 85 | ltx = ''' |
| 86 | \\documentclass{standalone} |
| 87 | \\usepackage{tikz-timing} |
| 88 | |
| 89 | \\begin{document} |
| 90 | \\begin{tikztimingtable}[timing/xunit=35,timing/yunit=10] |
| 91 | ''' |
| 92 | |
| 93 | def latexEscape(val): |
| 94 | return val.\ |
| 95 | replace("_", "\\_").\ |
| 96 | replace("{", "\\{").\ |
| 97 | replace("}", "\\}").\ |
| 98 | replace("&", "\\&").\ |
| 99 | replace("%", "\\%").\ |
| 100 | replace("$", "\\$").\ |
| 101 | replace("#", "\\#").\ |
| 102 | replace("$", "\\$") |
| 103 | |
| 104 | |
| 105 | for sig in range(len(data)): |
| 106 | sigName = latexEscape(all_signals[sig]) |
| 107 | sigSeries = data[sig] |
| 108 | sigWidth = dataW[sig] |
| 109 | # print(f"SigName: {sigName} - SigWidth: {sigWidth}") |
| 110 | ltx += format(f" {sigName} &") |
| 111 | for value in sigSeries: |
| 112 | prefix = "" |
| 113 | token = format(f" D{{{value}}} ") |
| 114 | if value == "x": |
| 115 | token = "X" |
| 116 | elif value == "z": |
| 117 | token = "Z" |
| 118 | elif sigWidth == 1: |
| 119 | token = "H" if value == "1" else "L" |
| 120 | if "x" in token: |
| 121 | prefix = " [red] " |
| 122 | elif "z" in token: |
| 123 | prefix = " [blue] " |
| 124 | |
| 125 | ltx += format(f" {prefix}{token} ;") |
| 126 | ltx += " \\\\\n" |
| 127 | |
| 128 | # ltx += "\\vertlines[help lines,opacity=0.3]{}\n" |
| 129 | ltx += '''\\extracode |
| 130 | \\vertlines[help lines,opacity=0.3]{} |
| 131 | \\end{tikztimingtable} |
| 132 | \\end{document} |
| 133 | |
| 134 | ''' |
| 135 | |
| 136 | # print(ltx) |
| 137 | with open("tmp.tex", "w") as f: |
| 138 | f.write(ltx) |
| 139 | |
| 140 | |
| 141 | ''' |
| 142 | \\begin{tikztimingtable}[timing/wscale=0.8] |
| 143 | M-cycle & X 8D{M1} 8D{M2/M1} X \\\\ |
| 144 | Instruction & ; [opacity=0.4] 9D{Previous} ; [opacity=1.0] 8D{LD r, r'} ; [opacity=0.4] X \\\\ |
| 145 | Mem R/W & X 8D{R: opcode}; [opacity=0.4] 8D{R: next op} X \\\\ |
| 146 | Mem addr & X 8D{PC} ; [opacity=0.4] 8D{PC+1} X \\\\ |
| 147 | \\end{tikztimingtable} |
| 148 | ''' |
| 149 |