Commit 14982ed1 authored by Martin Jeřábek's avatar Martin Jeřábek

testfw: use ghw_parse to properly generate GHW file from TCL

parent 4f2a4dbc
......@@ -76,8 +76,10 @@ def create():
@click.argument('vunit_args', nargs=-1)
@click.option('--strict/--no-strict', default=True,
help='Return non-zero if an unconfigured test was found.')
@click.option('--create-ghws/--no-create-ghws', default=False,
help='Only elaborate and create basic GHW files necessary for converting TCL layout files to GTKW files for gtkwave..')
@click.pass_obj
def test(obj, config, strict, vunit_args):
def test(obj, config, strict, create_ghws, vunit_args):
"""Run the tests. Configuration is passed in YAML config file.
You mas pass arguments directly to VUnit by appending them at the command end.
......@@ -108,6 +110,10 @@ def test(obj, config, strict, vunit_args):
build.mkdir(exist_ok=True)
os.chdir(str(build))
if create_ghws:
# discard the passed vunit_args, it does only evil
vunit_args = ['--elaborate']
ui = create_vunit(obj, vunit_args, out_basename)
lib = ui.add_library("lib")
......@@ -124,7 +130,7 @@ def test(obj, config, strict, vunit_args):
tests = []
for cfg_key, factory in tests_classes:
if cfg_key in config:
tests.append(factory(ui, lib, config[cfg_key], build, base))
tests.append(factory(ui, lib, config[cfg_key], build, base, create_ghws=create_ghws))
if not os.path.exists(func_cov_dir):
os.makedirs(func_cov_dir);
......
......@@ -4,6 +4,8 @@ from typing import List
import logging
import traceback
import functools
from pathlib import Path
from . import ghw_parse
log = logging.getLogger('gtkwave')
......@@ -20,8 +22,9 @@ def logexc(f):
class TclFuncs:
def __init__(self, gtkw: str):
def __init__(self, gtkw: str, hierarchy):
self.gtkw = gtkw
self.hierarchy = hierarchy
# set up TCL
tcl = tkinter.Tcl()
......@@ -39,9 +42,63 @@ class TclFuncs:
def tcl_quietly(self, *args):
return self.tcl.call(*args)
def sigtype(self, sig: str):
fqn = sig.replace('/', '.')
type = ghw_parse.find(self.hierarchy, fqn)
return type
def convsig(self, sig: str) -> str:
fqn = sig.replace('/', '.')
type = ghw_parse.find(self.hierarchy, fqn)
if ghw_parse.is_array(type):
range, type = ghw_parse.strip_array(type)
l, r = range.left, range.right
fqn += '({}:{})'.format(l, r)
fqn = 'top.' + fqn
return fqn.replace('(', '[').replace(')', ']')
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:
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.group = None
self.expand = False
self.color = None
def __init__(self):
self.reset()
@staticmethod
def convsig(sig: str) -> str:
return 'top.' + sig.replace('/', '.').replace('(', '[').replace(')', ']')
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)
@logexc
def tcl_add(self, *args):
......@@ -50,57 +107,62 @@ class TclFuncs:
raise ValueError("Unsupported add TCL command")
i += 1
label = None
format = 'hex'
signal = None
isdivider = False
group = None
o = self.Opts()
while i < len(args):
a0 = args[i]
i += 1
if a0 == '-label':
label = args[i]
o.label = args[i]
i += 1
elif a0 == '-hexadecimal':
format = 'hex'
o.format = 'hex'
elif a0 == '-unsigned':
format = 'dec'
o.format = 'dec'
elif a0 == '-decimal':
o.format = 'dec'
elif a0 == '-signed': # ????
format = 'signed'
o.format = 'signed'
elif a0 == '-noupdate':
pass
elif a0 == '-color':
o.color = self.conv_color(args[i])
i += 1
elif a0 == '-divider':
isdivider = True
o.isdivider = True
elif a0 == '-height':
i += 1
elif a0 == '-expand':
o.expand = True
elif a0 == '-group':
if group:
self.gtkw.end_group(group, closed=False)
group = args[i]
self.gtkw.begin_group(group, closed=False)
if o.group:
self.gtkw.end_group(o.group, closed=False)
o.group = args[i]
self.gtkw.begin_group(o.group, closed=False)
i += 1
elif a0[0] == '-':
raise ValueError("Unknown TCL add wave arg " + a0)
else:
signal = a0
if isdivider:
if o.isdivider:
self.gtkw.blank(label=signal)
else:
self.gtkw.trace(self.convsig(signal), alias=label, datafmt=format)
label = None
format = 'hex'
signal = None
isdivider = False
if group:
self.gtkw.end_group(group)
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:
self.gtkw.end_group(o.group)
def tcl2gtkw(tcl_wave, tcl_init_files: List[str], gtkw):
def tcl2gtkw(tcl_wave, tcl_init_files: List[str], gtkw, ghw: Path):
hierarchy = ghw_parse.parse(ghw)
with open(gtkw, 'wt') as f:
gtkw = GTKWSave(f)
gtkw.zoom_markers(-27.0)
c = TclFuncs(gtkw)
c = TclFuncs(gtkw, hierarchy)
c.tcl.createcommand('vunit_help', lambda: None)
for tcl in tcl_init_files:
c.source(tcl)
......
......@@ -21,12 +21,13 @@ jinja_env = Environment(loader=PackageLoader(__package__, 'data'), autoescape=Fa
class TestsBase:
def __init__(self, ui, lib, config, build, base):
def __init__(self, ui, lib, config, build, base, create_ghws: bool):
self.ui = ui
self.lib = lib
self.config = config
self.build = build
self.base = base
self.create_ghws = create_ghws
@property
def jinja_env(self):
......@@ -44,7 +45,7 @@ class TestsBase:
def add_modelsim_gui_file(self, tb, cfg, name, tcl_init_files: List[str] = None) -> None:
if tcl_init_files is None:
tcl_init_files = tb.get_sim_option("modelsim.init_files.after_load")
tcl_init_files = get_common_modelsim_init_files()
if 'wave' in cfg:
tcl = self.base / cfg['wave']
if not tcl.exists():
......@@ -72,11 +73,30 @@ class TestsBase:
else:
gtkw = tcl.with_suffix('.gtkw')
tclfname = tcl.relative_to(self.base)
log.info('Converting wave file {} to gtkw ...'.format(tclfname))
tcl2gtkw(str(tcl), tcl_init_files, str(gtkw))
ghw_file = self.build / (tb.name+'.elab.ghw')
# We need the GHW file for TCL -> GTKW conversion. If we are
# generating them, there is no sense in actually doing
# the conversion now.
if self.create_ghws:
log.info('Will generate {}'.format(ghw_file))
sim_flags = get_common_sim_flags()
sim_flags += ['--wave=' + str(ghw_file)]
tb.set_sim_option("ghdl.sim_flags", sim_flags)
else:
if not ghw_file.exists():
log.warning("Cannot convert wave file {} to gtkw, because"
" GHW file is missing. Run test with "
"--create-ghws.".format(tclfname))
gtkw = None
else:
log.info('Converting wave file {} to gtkw ...'.format(tclfname))
tcl2gtkw(str(tcl), tcl_init_files, str(gtkw), ghw_file)
if gtkw:
tb.set_sim_option("ghdl.gtkw_file", str(gtkw))
try:
tb.set_sim_option("ghdl.gtkw_file", str(gtkw))
except ValueError:
log.warning('Setting GTKW file per test is not supported in this VUnit version.')
def add_sources(lib, patterns) -> None:
for pattern in patterns:
......@@ -98,6 +118,9 @@ def get_common_modelsim_init_files() -> List[str]:
modelsim_init_files = [str(d/x) for x in modelsim_init_files.split(',')]
return modelsim_init_files
def get_common_sim_flags() -> List[str]:
return ["--ieee-asserts=disable-at-0"]
def add_flags(ui, lib, build) -> None:
unit_tests = lib.get_test_benches('*_unit_test', allow_empty=True)
for ut in unit_tests:
......@@ -117,7 +140,7 @@ def add_flags(ui, lib, build) -> None:
ui.set_sim_option("ghdl.elab_flags",elab_flags)
# Global simulation flags
sim_flags = ["--ieee-asserts=disable-at-0"]
sim_flags = get_common_sim_flags()
ui.set_sim_option("ghdl.sim_flags", sim_flags)
modelsim_init_files = get_common_modelsim_init_files()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment