Skip to content

Commit

Permalink
Refactor: new diff interface
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuanhao-wu committed Jan 13, 2020
1 parent 80f615b commit 626496a
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 10 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
lark-parser==0.7.8
pytest==5.0.1
pyverilog
61 changes: 53 additions & 8 deletions tests/verilog-conversion/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class SystemCClangDriver(object):
"-x c++ -w -c"
]



"""
Extra args are positional and non positional arguments, for example,
extra header etc
Expand All @@ -43,8 +41,48 @@ def __init__(self, conf, *args, **kwargs):
"""
Takes _hdl.txt as input, generate .v
"""
def generate_verilog_from_sexp(self, path):
pass
def generate_verilog_from_sexp(self, path, output_folder, keep_v=False, verbose=False):
if not path.endswith('_hdl.txt'):
raise RuntimeError('Filename should end with _hdl.txt')
v_loc = path + '.v'
v_loc = os.path.abspath(v_loc)
if os.path.isfile(v_loc):
raise RuntimeError('File to generate: {} exists'.format(v_loc))

v_filename = os.path.basename(v_loc)
output_filename = '{}/{}'.format(output_folder, v_filename)
output_filename = os.path.abspath(output_filename)
if os.path.isfile(output_filename):
raise RuntimeError('File to generate: {} exists'.format(v_loc))

path = os.path.abspath(path)
if not os.path.isfile(path):
raise RuntimeError('Sexp file {} does not exist'.format(path))

if verbose:
print('path: ', path)
print('v_loc: ', os.path.normpath(v_loc))
print('v_filename: ', v_filename)
print('output_filename: ', os.path.normpath(output_filename))

cmdline = ' '.join([
SystemCClangDriver.PYTHON_CONVERT_TEMPLATE,
path
])
try:
if verbose:
subprocess.run(cmdline, shell=True)
else:
with open(os.devnull, 'wb') as null:
subprocess.run(cmdline)
return True, output_filename
except:
raise
finally:
if not keep_v:
subprocess.run('rm {}'.format(output_filename), shell=True)
if keep_v and os.path.normpath(v_loc) != os.path.normpath(output_filename):
subprocess.run('rm -f {}'.format(output_filename), shell=True)

"""
Takes .cpp as input, generate _hdl.txt
Expand All @@ -71,7 +109,7 @@ def generate_sexp(self, path, output_folder, keep_sexp=False, verbose=False):
print('sexp_loc: ', sexp_loc)
print('sexp_filename: ', sexp_filename)
print('output_filename: ', output_filename)

ok = True
try:
if verbose:
subprocess.run(' '.join(cmdline), shell=True)
Expand All @@ -81,6 +119,9 @@ def generate_sexp(self, path, output_folder, keep_sexp=False, verbose=False):
stdout=null,
stderr=null)
subprocess.run('mv {} {}'.format(sexp_loc, output_folder), shell=True)
return True, output_filename
except:
raise
finally:
# we remove the generated file anyway
if not keep_sexp:
Expand All @@ -89,9 +130,13 @@ def generate_sexp(self, path, output_folder, keep_sexp=False, verbose=False):
if keep_sexp and os.path.normpath(sexp_loc) != os.path.normpath(output_filename):
subprocess.run('rm -f {}'.format(sexp_loc), shell=True)

return ok, output_filename

"""
Takes .cpp as input, generate .v
If any step fails, an exeption will be thrown
"""
def generate_verilog(self, path, output_folder):
generate_sexp(path)
pass
def generate_verilog(self, path, output_folder, verbose):
succ_sexp, sexp_filename = generate_sexp(path, output_folder, keep_sexp=True, verbose=verbose)
succ_v, v_filename = generate_verilog(sexp_filename, output_folder, keep_v=True, verbose=verbose)

9 changes: 7 additions & 2 deletions tests/verilog-conversion/run.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from driver import *
from util.vparser import VerilogParser
import tempfile
import contextlib

Expand Down Expand Up @@ -44,8 +45,12 @@ def main():
driver = SystemCClangDriver(conf=conf)
filename='~/working/systemc-clang/tests/data/verilog-conversion/llnl-examples/sreg.cpp'
# print(' '.join(driver.systemc_clang_commandline(filename=filename)))
tempfolder = tempfile.mkdtemp()
driver.generate_sexp(path=filename, output_folder='.', verbose=True, keep_sexp=True)
# tempfolder = tempfile.mkdtemp()
# driver.generate_sexp(path=filename, output_folder='.', verbose=True, keep_sexp=True)
# driver.generate_verilog_from_sexp(path='./sreg_hdl.txt', output_folder='.', verbose=True, keep_v=True)

res = VerilogParser.diff('./sreg_hdl.txt.v', './sreg_hdl.txt.new.v')
print('Are they same? ', res)

if __name__ == '__main__':
main()
Expand Down
81 changes: 81 additions & 0 deletions tests/verilog-conversion/util/vdiff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Testing utilities"""
from io import StringIO

class DiffInfo(object):
def __init__(self, lineno=None, diff_tuples=None, diff_nodes=None):
info = [diff_tuples, diff_nodes]
info = list(filter(lambda x: x is not None, info))
assert len(info) <= 1, 'At most one type of diff is accepted'
self.diff_tuples = diff_tuples
self.diff_nodes = diff_nodes
self.lineno = lineno

def __str__(self):
if self.diff_tuples:
res = "# Different Attributes at line {}: ".format(self.lineno)
res = res + '\n'.join("- {}: {}\n+ {}: {}".format(str(k1), str(v1), str(k2), str(v2))
for k1, v1, k2, v2 in self.diff_tuples)
return res
if self.diff_nodes:
def _get_str(node):
buf = StringIO()
node.show(buf=buf)
return buf.getvalue()
res = "# Different Children at line {}: \n".format(self.lineno)
res += "# In the first file\n"
res += ''.join(map(_get_str, self.diff_nodes[0])) + '\n'
res += "# In the next file\n"
res += ''.join(map(_get_str, self.diff_nodes[1]))
return res
return ""

class VerilogASTDiff(object):
"""interpret the diff of AST of verilog"""

@staticmethod
def _diff_traverse(this_node, that_node):
"""traverses two asts using dfs, stops when different node encountered"""
print("at: {}".format(this_node))
if type(this_node) != type(that_node):
return False

# assuming each attr_name appear once in each node
this_names = set(this_node.attr_names)
that_names = set(that_node.attr_names)
diff = []
for a in this_names:
this_attr = getattr(this_node, a)
if a in that_names:
that_attr = getattr(that_node, a)
if this_attr != that_attr:
print(this_attr, that_attr)
diff.append((a, this_attr, a, that_attr))
else:
diff.append((a, this_attr, '', ''))
for a in that_names:
that_attr = getattr(that_node, a)
if a not in this_names:
diff.append(('', '', a, that_attr))

if diff:
return DiffInfo(this_node.lineno, diff_tuples=diff)

other_children = that_node.children()

if len(this_node.children()) != len(other_children):
return DiffInfo(this_node.lineno,
diff_nodes = [this_node.children(),
other_children])

for i, c in enumerate(this_node.children()):
res = VerilogASTDiff._diff_traverse(c, other_children[i])
if res:
return res

return None


@staticmethod
def diff_info(this_verilog_ast, that_verilog_ast):
"""shows the diff information of two ast, including line number and node name"""
return VerilogASTDiff._diff_traverse(this_verilog_ast, that_verilog_ast)
38 changes: 38 additions & 0 deletions tests/verilog-conversion/util/vparser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Testing utilities"""
import pyverilog.utils.version
from util.vdiff import VerilogASTDiff
from pyverilog.vparser.parser import parse

class VerilogParser(object):
"""parses Verilog code into AST, useful for comparing verilogs"""

@staticmethod
def parse(verilog_filename, include_list=None, define_list=None):
"""
parses verilog code
"""
if include_list is None:
include_list = []
if define_list is None:
define_list = []
ast, _ = parse([ verilog_filename ],
preprocess_include=include_list,
preprocess_define=define_list)
return ast

@staticmethod
def diff(this_verilog_filename, that_verilog_filename,
include_list=None, define_list=None):
"""
parses verilog_a and verilog_b and outputs their diff
"""
this_ast = VerilogParser.parse(this_verilog_filename, include_list, define_list)
that_ast = VerilogParser.parse(that_verilog_filename, include_list, define_list)

this_ast.show()
that_ast.show()

# show the diff
diff_info = VerilogASTDiff.diff_info(this_ast, that_ast)
return diff_info

0 comments on commit 626496a

Please sign in to comment.