From 15104a0f8c536ca42d5263c6a64c5c5b65a49297 Mon Sep 17 00:00:00 2001 From: Josy Boelen Date: Thu, 19 Oct 2023 17:00:26 +0200 Subject: [PATCH] **Constant** signals (#423) * Added `Constant` signal object * Corrected *_shortversion* ToDo: replace by single global variable in myhdl/myhdl/__init__.py * one more *_shortversion* ... --- myhdl/_Signal.py | 40 ++++++++++++-- myhdl/__init__.py | 8 +-- myhdl/_always_comb.py | 7 ++- myhdl/conversion/_toVHDL.py | 53 ++++++++++++------- myhdl/conversion/_toVHDLPackage.py | 4 +- myhdl/conversion/_toVerilog.py | 39 +++++++++----- myhdl/conversion/_verify.py | 2 +- myhdl/test/conversion/general/test_set_dir.py | 2 +- 8 files changed, 106 insertions(+), 49 deletions(-) diff --git a/myhdl/_Signal.py b/myhdl/_Signal.py index a1e3d975..5d85eb36 100644 --- a/myhdl/_Signal.py +++ b/myhdl/_Signal.py @@ -180,8 +180,8 @@ class _Signal(object): self._val = deepcopy(self._init) self._next = deepcopy(self._init) self._name = self._driven = None - self._read = False # dont clear self._used - self._inList = False + self._read = False # dont clear self._used + self._inList = False self._numeric = True for s in self._slicesigs: s._clear() @@ -268,7 +268,7 @@ class _Signal(object): @read.setter def read(self, val): - if not val in (True, ): + if not val in (True,): raise ValueError('Expected value True, got "%s"' % val) self._markRead() @@ -552,7 +552,7 @@ class _Signal(object): def __setitem__(self, key, val): raise TypeError("Signal object doesn't support item/slice assignment") - # continues assignment support + # continuous assignment support def assign(self, sig): self.driven = "wire" @@ -637,6 +637,38 @@ class _SignalWrap(object): def apply(self): return self.sig._apply(self.next, self.timeStamp) + +class Constant(_Signal): + ''' effective constants ''' + + def __init__(self, val=None): + super(Constant, self).__init__(val) + + # override some essentials + def __repr__(self): + return "Constant(" + repr(self._val) + ")" + + # there is support for the 'next' attribute + @property + def next(self): + return None + + @next.setter + def next(self, val): + raise PermissionError("A 'Constant' can not be changed!") + + # neither can it be driven + # support for the 'driven' attribute + @property + def driven(self): + return None + + @driven.setter + def driven(self, val): + # quietly ignore? + pass + + # for export SignalType = _Signal diff --git a/myhdl/__init__.py b/myhdl/__init__.py index ef4e75dd..caa72297 100644 --- a/myhdl/__init__.py +++ b/myhdl/__init__.py @@ -49,7 +49,7 @@ traceSignals -- function that enables signal tracing in a VCD file toVerilog -- function that converts a design to Verilog """ -__version__ = "0.11.42" +__version__ = "0.11.43" class StopSimulation(Exception): @@ -139,7 +139,7 @@ class ToVHDLWarning(ConversionWarning): # def showwarning(message, category, filename, lineno, *args): # print("** %s: %s" % (category.__name__, message), file=sys.stderr) -#warnings.showwarning = showwarning +# warnings.showwarning = showwarning from ._bin import bin @@ -147,7 +147,7 @@ from ._concat import concat from ._intbv import intbv from ._modbv import modbv from ._join import join -from ._Signal import posedge, negedge, Signal, SignalType +from ._Signal import posedge, negedge, Signal, SignalType, Constant from ._ShadowSignal import ConcatSignal from ._ShadowSignal import TristateSignal from ._simulator import now @@ -169,7 +169,6 @@ from .conversion import toVHDL from ._tristate import Tristate - __all__ = ["bin", "concat", "intbv", @@ -179,6 +178,7 @@ __all__ = ["bin", "negedge", "Signal", "SignalType", + "Constant", "ConcatSignal", "TristateSignal", "now", diff --git a/myhdl/_always_comb.py b/myhdl/_always_comb.py index d1b93577..7d9b8e0e 100644 --- a/myhdl/_always_comb.py +++ b/myhdl/_always_comb.py @@ -21,7 +21,7 @@ from types import FunctionType from myhdl import AlwaysCombError -from myhdl._Signal import _Signal, _isListOfSigs +from myhdl._Signal import _Signal, _isListOfSigs, Constant from myhdl._util import _isGenFunc from myhdl._instance import _getCallInfo from myhdl._always import _Always @@ -62,9 +62,9 @@ class _AlwaysComb(_Always): for n in self.inputs: s = self.symdict[n] - if isinstance(s, _Signal): + if isinstance(s, _Signal) and not isinstance(s, Constant): senslist.append(s) - elif _isListOfSigs(s): + elif _isListOfSigs(s) and not isinstance(s[0], Constant): senslist.extend(s) self.senslist = tuple(senslist) if len(self.senslist) == 0: @@ -79,4 +79,3 @@ class _AlwaysComb(_Always): func() yield senslist - diff --git a/myhdl/conversion/_toVHDL.py b/myhdl/conversion/_toVHDL.py index 031e5e6c..242716c7 100644 --- a/myhdl/conversion/_toVHDL.py +++ b/myhdl/conversion/_toVHDL.py @@ -43,7 +43,7 @@ from myhdl._extractHierarchy import (_HierExtr, _isMem, _getMemInfo, _UserVhdlCode, _userCodeMap) from myhdl._instance import _Instantiator -from myhdl._Signal import _Signal, _WaiterList, posedge, negedge +from myhdl._Signal import _Signal, _WaiterList, posedge, negedge, Constant from myhdl._enum import EnumType, EnumItemType from myhdl._intbv import intbv from myhdl._modbv import modbv @@ -65,7 +65,7 @@ from myhdl.conversion._VHDLNameValidation import _nameValid, _usedNames from myhdl import bin as tobin _version = myhdl.__version__.replace('.', '') -_shortversion = _version.replace('dev', '') +_shortversion = _version.replace('dev', '')[:-2] # loose the subminor version number _converting = 0 _profileFunc = None _enumPortTypeSet = set() @@ -498,11 +498,21 @@ def _writeSigDecls(f, intf, siglist, memlist): # the original exception # raise ToVHDLError(_error.UndrivenSignal, s._name) # changed to a warning and a continuous assignment to a wire - warnings.warn("%s: %s" % (_error.UndrivenSignal, s._name), - category=ToVHDLWarning - ) - constwires.append(s) - print("signal %s: %s%s;" % (s._name, p, r), file=f) + if isinstance(s, Constant): + if isinstance(s._val, intbv): + if s._init: + print('constant %s: %s%s := %dX"%s" /* %s */;' % (s._name, p, r, s._nrbits, str(s._init), int(s._init)), file=f) + else: + print('constant %s: %s%s := %dX"0";' % (s._name, p, r, s._nrbits), file=f) + else: + print("constant %s: %s%s := %s;" % (s._name, p, r, "'1'" if s._init else "'0'"), file=f) + else: + warnings.warn("%s: %s" % (_error.UndrivenSignal, s._name), + category=ToVHDLWarning + ) + constwires.append(s) + print("signal %s: %s%s;" % (s._name, p, r), file=f) + for m in memlist: if not m._used: continue @@ -520,7 +530,7 @@ def _writeSigDecls(f, intf, siglist, memlist): p = _getTypeString(m.elObj) t = "t_array_%s" % m.name - if not toVHDL.initial_values: + if not toVHDL.initial_values and not isinstance(m.mem[0], Constant): val_str = "" else: sig_vhdl_objs = [inferVhdlObj(each) for each in m.mem] @@ -542,7 +552,10 @@ def _writeSigDecls(f, intf, siglist, memlist): val_str = ' := (\n ' + _val_str + ')' print("type %s is array(0 to %s-1) of %s%s;" % (t, m.depth, p, r), file=f) - print("signal %s: %s%s;" % (m.name, t, val_str), file=f) + if isinstance(m.mem[0], Constant): + print("constant %s: %s%s;" % (m.name, t, val_str), file=f) + else: + print("signal %s: %s%s;" % (m.name, t, val_str), file=f) print(file=f) @@ -890,7 +903,7 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): lpre, lsuf = self.inferCast(node.dest.vhd, node.left.vhd) if isinstance(node.right, ast.Name): rpre, rsuf = self.inferCast(node.dest.vhd, node.right.vhd) - + self.write("(") self.write(lpre) self.visit(node.left) @@ -921,13 +934,13 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): node.operand.vhd = node.vhd self.visit(node.operand) return - + pre, suf = self.inferCast(node.vhd, node.vhdOri) if isinstance(node.op, ast.UAdd): op = "" else: op = opmap[type(node.op)] - + if isinstance(node.operand, ast.Constant): self.write("(") self.write(op) @@ -950,13 +963,13 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): node.operand.vhd = node.vhd self.visit(node.operand) return - + pre, suf = self.inferCast(node.vhd, node.vhdOri) if isinstance(node.op, ast.UAdd): op = "" else: op = opmap[type(node.op)] - + if isinstance(node.operand, ast.Num): self.write("(") self.write(op) @@ -1441,7 +1454,7 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.mapToCase(node) else: self.mapToIf(node) - + def visit_Match(self, node): self.write("case ") self.visit(node.subject) @@ -1454,7 +1467,7 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.dedent() self.writeline() self.write("end case;") - + def visit_match_case(self, node): self.writeline() self.write("when ") @@ -1472,10 +1485,10 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.dedent() def visit_MatchValue(self, node): - baseobj = self.getObj(node.subject) + baseobj = self.getObj(node.subject) item = node.value obj = self.getObj(item) - + if isinstance(obj, EnumItemType): itemRepr = obj._toVHDL() elif hasattr(baseobj, '_nrbits'): @@ -1506,12 +1519,12 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.write("others") else: raise AssertionError("Unknown name %s or pattern %s" % (node.name, node.pattern)) - + def visit_MatchOr(self, node): for i, pattern in enumerate(node.patterns): pattern.subject = node.subject self.visit(pattern) - if not i == len(node.patterns)-1: + if not i == len(node.patterns) - 1: self.write(" | ") def mapToCase(self, node): diff --git a/myhdl/conversion/_toVHDLPackage.py b/myhdl/conversion/_toVHDLPackage.py index e2d0cea5..ac6d2fb3 100644 --- a/myhdl/conversion/_toVHDLPackage.py +++ b/myhdl/conversion/_toVHDLPackage.py @@ -20,7 +20,7 @@ import myhdl _version = myhdl.__version__.replace('.', '') -_shortversion = _version.replace('dev', '') +_shortversion = _version.replace('dev', '')[:-2] _package = """\ library ieee; @@ -191,4 +191,4 @@ package body pck_myhdl_%(version)s is end pck_myhdl_%(version)s; -""" % {'version' : _shortversion} +""" % {'version': _shortversion} diff --git a/myhdl/conversion/_toVerilog.py b/myhdl/conversion/_toVerilog.py index 227c9469..9b76b6df 100644 --- a/myhdl/conversion/_toVerilog.py +++ b/myhdl/conversion/_toVerilog.py @@ -45,7 +45,7 @@ from myhdl.conversion._misc import (_error, _kind, _context, _ConversionMixin, _Label, _genUniqueSuffix, _isConstant) from myhdl.conversion._analyze import (_analyzeSigs, _analyzeGens, _analyzeTopFunc, _Ram, _Rom) -from myhdl._Signal import _Signal +from myhdl._Signal import _Signal, Constant from myhdl._ShadowSignal import _TristateSignal, _TristateDriver from myhdl._block import _Block @@ -340,14 +340,20 @@ def _writeSigDecls(f, intf, siglist, memlist): print("%s %s%s%s = %s;" % (k, p, r, s._name, _intRepr(s._init)), file=f) elif s._read: - # the original exception - # raise ToVerilogError(_error.UndrivenSignal, s._name) - # changed to a warning and a continuous assignment to a wire - warnings.warn("%s: %s" % (_error.UndrivenSignal, s._name), - category=ToVerilogWarning - ) - constwires.append(s) - print("wire %s%s;" % (r, s._name), file=f) + if isinstance(s, Constant): + c = int(s.val) + c_len = s._nrbits + c_str = "%s" % c + print("localparam %s%s = %s'd%s;" % (r, s._name, c_len, c_str), file=f) + else: + # the original exception + # raise ToVerilogError(_error.UndrivenSignal, s._name) + # changed to a warning and a continuous assignment to a wire + warnings.warn("%s: %s" % (_error.UndrivenSignal, s._name), + category=ToVerilogWarning + ) + constwires.append(s) + print("wire %s%s;" % (r, s._name), file=f) # print(file=f) for m in memlist: if not m._used: @@ -392,9 +398,16 @@ def _writeSigDecls(f, intf, siglist, memlist): for n, each in enumerate(m.mem)]) initial_assignments = ( 'initial begin\n' + val_assignments + '\nend') + print("%s %s%s%s [0:%s-1];" % (k, p, r, m.name, m.depth), file=f) + else: + # can assume it is a localparam array + # build the initial values list + vals = [] + w = m.mem[0]._nrbits + for s in m.mem: + vals.append('{}\'d{}'.format(w, _intRepr(s._init))) - print("%s %s%s%s [0:%s-1];" % (k, p, r, m.name, m.depth), - file=f) + print('localparam {} {} {} [0:{}-1] = {{{}}};'.format(p, r, m.name, m.depth, ', '.join(vals)), file=f) if initial_assignments is not None: print(initial_assignments, file=f) @@ -1100,11 +1113,11 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.write("default") else: raise AssertionError("Unknown name %s or pattern %s" % (node.name, node.pattern)) - + def visit_MatchOr(self, node): for i, pattern in enumerate(node.patterns): self.visit(pattern) - if not i == len(node.patterns)-1: + if not i == len(node.patterns) - 1: self.write(" | ") def mapToCase(self, node, *args): diff --git a/myhdl/conversion/_verify.py b/myhdl/conversion/_verify.py index db742f7a..631c03ef 100644 --- a/myhdl/conversion/_verify.py +++ b/myhdl/conversion/_verify.py @@ -15,7 +15,7 @@ from myhdl._block import _Block _version = myhdl.__version__.replace('.', '') # strip 'dev' for version -_version = _version.replace('dev', '') +_version = _version.replace('dev', '')[:-2] _simulators = {} diff --git a/myhdl/test/conversion/general/test_set_dir.py b/myhdl/test/conversion/general/test_set_dir.py index 35aeefa8..c70168fa 100644 --- a/myhdl/test/conversion/general/test_set_dir.py +++ b/myhdl/test/conversion/general/test_set_dir.py @@ -7,7 +7,7 @@ from myhdl import (block, Signal, intbv, always, toVHDL, toVerilog) from myhdl import __version__ _version = __version__.replace('.', '') -_shortversion = _version.replace('dev', '') +_shortversion = _version.replace('dev', '')[:-2] @block