diff --git a/myhdl/conversion/_VHDLNameValidation.py b/myhdl/conversion/_VHDLNameValidation.py index e8064052..7a4d15cc 100644 --- a/myhdl/conversion/_VHDLNameValidation.py +++ b/myhdl/conversion/_VHDLNameValidation.py @@ -1,6 +1,7 @@ import warnings # from myhdl import * -# from myhdl import ToVHDLWarning + +from myhdl import ToVHDLWarning # A list of all reserved words within VHDL which should not be used for # anything other than their own specific purpose @@ -33,16 +34,19 @@ _usedNames = []; # ensure reserved words are not being used for the wrong purpose def _nameValid(name): if name.lower() in _vhdl_keywords: - warnings.warn("VHDL keyword used: %s" % (name)) + warnings.warn("VHDL keyword used: {}".format(name), category=ToVHDLWarning) if name.startswith('_'): - warnings.warn("VHDL variable names cannot start with '_': %s" % (name)) + warnings.warn("VHDL variable names cannot start with '_': {}".format(name), category=ToVHDLWarning) if '-' in name: - warnings.warn("VHDL variable names cannot contain '-': %s" % (name)) + warnings.warn("VHDL variable names cannot contain '-': {}".format(name), category=ToVHDLWarning) + + if '__' in name: + warnings.warn("VHDL variable names cannot contain double underscores '__': {}".format(name), category=ToVHDLWarning) if name.lower() in _usedNames: - warnings.warn("Previously used name being reused: %s" % (name)) + warnings.warn("Previously used name being reused: {}".format(name), category=ToVHDLWarning) _usedNames.append(name.lower()) diff --git a/myhdl/conversion/_toVHDL.py b/myhdl/conversion/_toVHDL.py index a5c2727c..5deb1285 100644 --- a/myhdl/conversion/_toVHDL.py +++ b/myhdl/conversion/_toVHDL.py @@ -63,7 +63,7 @@ from myhdl.conversion._misc import (_error, _kind, _context, from myhdl.conversion._analyze import (_analyzeSigs, _analyzeGens, _analyzeTopFunc, _Ram, _Rom, _enumTypeSet) from myhdl.conversion._toVHDLPackage import _package -from myhdl.conversion._VHDLNameValidation import _nameValid +from myhdl.conversion._VHDLNameValidation import _nameValid, _usedNames from myhdl import bin as tobin @@ -151,6 +151,9 @@ class _ToVHDLConvertor(object): if not callable(func): raise ToVHDLError(_error.FirstArgType, "got %s" % type(func)) + # clear out the list of user declared Signal (and other?) names + del _usedNames[:] + _converting = 1 if self.name is None: name = func.__name__ @@ -456,6 +459,8 @@ def _writeSigDecls(f, intf, siglist, memlist): continue r = _getRangeString(s) p = _getTypeString(s) + # Check if VHDL keyword or reused name + _nameValid(s._name) if s._driven: if not s._read and not isinstance(s, _TristateDriver): warnings.warn("%s: %s" % (_error.UnreadSignal, s._name), @@ -488,8 +493,6 @@ def _writeSigDecls(f, intf, siglist, memlist): print("signal %s: %s%s%s;" % (s._name, p, r, val_str), file=f) elif s._read: - # Check if VHDL keyword or reused name - _nameValid(s._name) # the original exception # raise ToVHDLError(_error.UndrivenSignal, s._name) # changed to a warning and a continuous assignment to a wire diff --git a/myhdl/test/conversion/toVHDL/test_keywords.py b/myhdl/test/conversion/toVHDL/test_keywords.py new file mode 100644 index 00000000..e1031045 --- /dev/null +++ b/myhdl/test/conversion/toVHDL/test_keywords.py @@ -0,0 +1,177 @@ +from __future__ import absolute_import +import myhdl +from myhdl import * +from myhdl import ToVHDLWarning + +import pytest +import tempfile +import shutil +import sys +import string +import importlib +import os +from keyword import kwlist as python_kwlist + +import warnings + +_vhdl_keywords = ["abs", "access", "after", "alias", "all", + "and", "architecture", "array", "assert", + "attribute", "begin", "block", "body", "buffer", + "bus", "case", "component", "configuration", + "constant", "disconnect", "downto", "else", + "elseif", "end", "entity", "exit", "file", "for", + "function", "generate", "generic", "group", + "guarded", "if", "impure", "in", "inertial", + "inout", "is", "label", "library", "linkage", + "literal", "loop", "map", "mod", "nand", "new", + "next", "nor", "not", "null", "of", "on", "open", + "or", "others", "out", "package", "port", + "postponed", "procedure", "process", "pure", + "range", "record", "register", "reject", "rem", + "report", "return", "rol", "ror", "select", + "severity", "signal", "shared", "sla", "sll", "sra", + "srl", "subtype", "then", "to", "transport", "type", + "unaffected", "units", "until", "use", "variable", + "wait", "when", "while", "with", "xnor", "xor"]; + +keyword_code = """ +from myhdl import * + +@block +def invalid_import_keyword(input_sig, output_sig): + ${keyword} = Signal(False) + + @always_comb + def do_something(): + ${keyword}.next = input_sig and input_sig + + @always_comb + def something_else(): + output_sig.next = ${keyword} + + return do_something, something_else +""" + + +@block +def invalid_signal_underscore(input_sig, output_sig): + _foo = Signal(bool(0)) + + @always_comb + def do_something(): + _foo.next = input_sig and input_sig + + @always_comb + def something_else(): + output_sig.next = _foo + + return do_something, something_else + + +@block +def invalid_function_underscore(clock, input_sig, output_sig): + + ttt = Signal(bool(0)) + + block1 = invalid_signal_underscore(input_sig, ttt) + + @always(clock.posedge) + def do_something(): + output_sig.next = ttt + + return block1, do_something + + +@block +def valid(input_sig, output_sig): + + @always_comb + def do_something(): + output_sig.next = input_sig + + return do_something + + +def test_multiple_conversion(): + sig_1 = Signal(True) + sig_2 = Signal(True) + + a_block = valid(sig_1, sig_2) + + # conversions with keyword should fail + with warnings.catch_warnings() as w: + warnings.simplefilter('error') + + a_block.convert(hdl='VHDL') + a_block.convert(hdl='VHDL') + + +def test_invalid_keyword_name(): + sig_1 = Signal(True) + sig_2 = Signal(True) + + temp_directory = tempfile.mkdtemp() + sys.path.append(temp_directory) + + keyword_template = string.Template(keyword_code) + + try: + for keyword in _vhdl_keywords: + if keyword in python_kwlist: + continue + + fd, full_filename = tempfile.mkstemp( + suffix='.py', dir=temp_directory) + + os.write(fd, keyword_template.substitute(keyword=keyword).encode('utf-8')) + os.close(fd) + + module_name = os.path.basename(full_filename)[:-3] # chop off .py + keyword_import = importlib.import_module(module_name) + + a_block = keyword_import.invalid_import_keyword(sig_1, sig_2) + + with pytest.warns(ToVHDLWarning): + a_block.convert(hdl='VHDL') + + finally: + sys.path.pop() + shutil.rmtree(temp_directory) + + +def test_invalid_signal_underscore_name(): + sig_1 = Signal(True) + sig_2 = Signal(True) + + a_block = invalid_signal_underscore(sig_1, sig_2) + + # Multiple conversions of a valid block should pass without warning + with pytest.warns(ToVHDLWarning): + a_block.convert(hdl='VHDL') + + +def test_invalid_function_underscore_name(): + sig_1 = Signal(True) + sig_2 = Signal(True) + clock = Signal(True) + + a_block = invalid_function_underscore(clock, sig_1, sig_2) + + # Multiple conversions of a valid block should pass without warning + with pytest.warns(ToVHDLWarning): + a_block.convert(hdl='VHDL') + + +if __name__ == '__main__': + sig_1 = Signal(True) + sig_2 = Signal(True) + + a_block = invalid_signal_underscore(sig_1, sig_2) + a_block.convert(hdl='VHDL') + + clock = Signal(True) + + a_block = invalid_function_underscore(clock, sig_1, sig_2) + + # Multiple conversions of a valid block should pass without warning + a_block.convert(hdl='VHDL')