gtkwave.py 5.99 KB
Newer Older
1 2
from vcd.gtkw import GTKWSave
import tkinter
3
from typing import List, Set
4 5 6
import logging
import traceback
import functools
7
import re
8 9
from pathlib import Path
from . import ghw_parse
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

log = logging.getLogger('gtkwave')


def logexc(f):
    @functools.wraps(f)
    def wrapper(*args, **kwds):
        try:
            return f(*args, **kwds)
        except:
            traceback.print_exc()
            raise
    return wrapper


class TclFuncs:
26
    def __init__(self, gtkw: str, hierarchy):
27
        self.gtkw = gtkw
28
        self.used_signals = set()  # type: Set[str]
29
        self.hierarchy = hierarchy
30 31 32 33 34 35 36 37 38 39 40

        # set up TCL
        tcl = tkinter.Tcl()
        self.tcl = tcl
        tcl.createcommand("add", self.tcl_add)
        tcl.createcommand('quietly', self.tcl_quietly)

    def source(self, file: str):
        log.debug('Sourcing '+file)
        self.tcl.eval('source "{}"'.format(file))

41 42 43
    def eval(self, code: str):
        self.tcl.eval(code)

44 45 46 47 48 49
    def finalize(self):
        pass

    def tcl_quietly(self, *args):
        return self.tcl.call(*args)

50 51
    def sigtype(self, sig: str):
        fqn = sig.replace('/', '.')
52
        fqn = re.sub(r'__([0-9]+)', r'(\1)', fqn)
53 54 55 56 57
        type = ghw_parse.find(self.hierarchy, fqn)
        return type

    def convsig(self, sig: str) -> str:
        fqn = sig.replace('/', '.')
58
        fqn = re.sub(r'__([0-9]+)', r'(\1)', fqn)
59 60
        type = ghw_parse.find(self.hierarchy, fqn)
        if ghw_parse.is_array(type):
61 62 63 64
            ranges, type = ghw_parse.strip_array(type)
            if len(ranges) > 1:
                raise NotImplementedError("Multidimensional arrays are not supported (yet)")
            l, r = ranges[0].left, ranges[0].right
65 66
            fqn += '({}:{})'.format(l, r)
        fqn = 'top.' + fqn
67
        return fqn.replace('(', '[').replace(')', ']').lower()
68

69 70 71 72 73 74 75
    def convsig_wave_opt(self, sig: str) -> str:
        sig = re.sub(r'__([0-9]+)', r'(\1)', sig)
        sig = re.sub(r'\([^)]+\)', '', sig)
        if sig[0] != '/':
            sig = '/'+sig
        return sig

76 77 78 79 80 81 82
    def _add_trace(self, signal, type, *, label: str, datafmt: str, expand: bool, **kwds):
        if ghw_parse.is_record(type):
            with self.gtkw.group(label, closed=not expand):
                for iname, itype in type.items.items():
                    # do not pass label
                    self._add_trace(signal+'/'+iname, itype, datafmt=datafmt, expand=False, label=None, **kwds)
        else:
83
            self.used_signals.add(self.convsig_wave_opt(signal))
84 85 86 87 88 89 90 91 92 93 94 95 96
            signal = self.convsig(signal)
            self.gtkw.trace(signal, alias=label, datafmt=datafmt, **kwds)

    class Opts:
        def reset(self):
            self.label = None
            self.format = 'hex'
            self.signal = None
            self.isdivider = False
            self.expand = False
            self.color = None

        def __init__(self):
97
            self.group = None
98 99
            self.reset()

100
    @staticmethod
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    def conv_color(clr: str) -> int:
        colors = [
            'normal',
            'red',
            'orange',
            'yellow',
            'green',
            'blue',
            'indigo',
            'violet',
            'cycle',
        ]
        mapping = {
            'cyan': 'yellow'
        }
        clr = clr.lower()
        clr = mapping.get(clr)
        return colors.index(clr)
119 120 121 122 123 124 125 126

    @logexc
    def tcl_add(self, *args):
        i = 0
        if args[i] != 'wave':
            raise ValueError("Unsupported add TCL command")
        i += 1

127
        o = self.Opts()
128 129 130 131
        while i < len(args):
            a0 = args[i]
            i += 1
            if a0 == '-label':
132
                o.label = args[i]
133 134
                i += 1
            elif a0 == '-hexadecimal':
135
                o.format = 'hex'
136
            elif a0 == '-unsigned':
137 138 139
                o.format = 'dec'
            elif a0 == '-decimal':
                o.format = 'dec'
140
            elif a0 == '-signed':  # ????
141
                o.format = 'signed'
142 143
            elif a0 == '-noupdate':
                pass
144 145 146
            elif a0 == '-color':
                o.color = self.conv_color(args[i])
                i += 1
147
            elif a0 == '-divider':
148
                o.isdivider = True
149 150
            elif a0 == '-height':
                i += 1
151 152
            elif a0 == '-expand':
                o.expand = True
153 154
            elif a0 == '-event':
                pass
155
            elif a0 == '-group':
156
                if o.group:
157
                    log.debug('Closing group {}'.format(o.group))
158 159
                    self.gtkw.end_group(o.group, closed=False)
                o.group = args[i]
160
                log.debug('Opening group {}'.format(o.group))
161
                self.gtkw.begin_group(o.group, closed=False)
162 163 164 165 166
                i += 1
            elif a0[0] == '-':
                raise ValueError("Unknown TCL add wave arg " + a0)
            else:
                signal = a0
167
                if o.isdivider:
168 169
                    self.gtkw.blank(label=signal)
                else:
170 171 172 173 174 175 176 177
                    try:
                        type = self.sigtype(signal)
                    except KeyError as e:
                        log.warning(e.args[0])
                        break
                    self._add_trace(signal, type, label=o.label, datafmt=o.format, expand=o.expand, color=o.color)
                o.reset()
        if o.group:
178
            log.debug('Closing group {}'.format(o.group))
179
            self.gtkw.end_group(o.group)
180 181


182
def tcl2gtkw(tcl_wave, tcl_init_files: List[str], gtkw, ghw: Path) -> List[str]:
183
    hierarchy = ghw_parse.parse(ghw)
184 185 186
    with open(gtkw, 'wt') as f:
        gtkw = GTKWSave(f)
        gtkw.zoom_markers(-27.0)
187
        c = TclFuncs(gtkw, hierarchy)
188
        c.tcl.eval('set SILENT_SANITY "false"')  # TODO: propagate from YML config
189 190 191 192 193 194
        c.tcl.createcommand('vunit_help', lambda: None)
        for tcl in tcl_init_files:
            c.source(tcl)
        c.tcl.createcommand('run_simulation', lambda: None)
        c.source(tcl_wave)
        c.finalize()
195 196
    used_signals = sorted(c.used_signals)
    return used_signals