diff --git a/myhdl/__init__.py b/myhdl/__init__.py index caa72297..649992a4 100644 --- a/myhdl/__init__.py +++ b/myhdl/__init__.py @@ -162,6 +162,7 @@ from ._instance import instance from ._block import block from ._enum import enum, EnumType, EnumItemType from ._traceSignals import traceSignals +from ._openport import OpenPort from myhdl import conversion from .conversion import toVerilog @@ -201,5 +202,6 @@ __all__ = ["bin", "toVerilog", "toVHDL", "conversion", - "Tristate" + "Tristate", + "OpenPort" ] diff --git a/myhdl/_openport.py b/myhdl/_openport.py new file mode 100644 index 00000000..06926d7e --- /dev/null +++ b/myhdl/_openport.py @@ -0,0 +1,65 @@ +''' +Created on 19 okt. 2023 + +@author: josy +''' + +from myhdl._Signal import _Signal + + +class OpenPort(_Signal): + ''' + helper for VHDL style 'open' ports + This avoids warnings of outputs 'driven but not read' + both in the conversion process and + later in synthesis by the Vendor tool, although those warnings will be buried + by the million ones coming from the Vendor IP ... + ''' + + # :code + def __init__(self): + ''' : + OpenPort() has no arguments + ''' + super(OpenPort, self).__init__(bool(0)) + self.__name = None + self._read = True + + @property + def _name(self): + ''' rely on toVerilog to replace the VHDL comment operator '--' with '//' ''' + return '-- OpenPort ' + self.__name if self.__name else None + + @_name.setter + def _name(self, name): + self.__name = name + + @property + def next(self): + pass + + @next.setter + def next(self, val): + ''' discard any 'new' value ''' + pass + + # override the Signal.driven property + @property + def driven(self): + pass + + @driven.setter + def driven(self, val): + pass + + @property + def val(self): + ''' an OpenPort object should not be read + but it may feed another 'Open' object? + returning 0 satisfies both bool() and intbv() + ''' + return 0 + + +if __name__ == '__main__': + pass diff --git a/myhdl/conversion/_toVHDL.py b/myhdl/conversion/_toVHDL.py index 242716c7..dc2c309f 100644 --- a/myhdl/conversion/_toVHDL.py +++ b/myhdl/conversion/_toVHDL.py @@ -390,11 +390,7 @@ def _writeModuleHeader(f, intf, needPck, lib, arch, useClauses, doc, stdLogicPor # # Check if VHDL keyword or reused name # _nameValid(s._name) if s._driven: - if s._read: - if not isinstance(s, _TristateSignal): - warnings.warn("%s: %s" % (_error.OutputPortRead, portname), - category=ToVHDLWarning - ) + if s._read and isinstance(s, _TristateSignal): f.write("\n %s: inout %s%s" % (portname, pt, r)) else: f.write("\n %s: out %s%s" % (portname, pt, r)) @@ -457,8 +453,14 @@ def _writeSigDecls(f, intf, siglist, memlist): for s in siglist: if not s._used: continue + if s._name in intf.argnames: continue + + if s._name.startswith('-- OpenPort'): + # do not write a signal declaration + continue + r = _getRangeString(s) p = _getTypeString(s) # Check if VHDL keyword or reused name @@ -495,9 +497,6 @@ def _writeSigDecls(f, intf, siglist, memlist): print("signal %s: %s%s%s;" % (s._name, p, r, val_str), file=f) elif s._read: - # the original exception - # raise ToVHDLError(_error.UndrivenSignal, s._name) - # changed to a warning and a continuous assignment to a wire if isinstance(s, Constant): if isinstance(s._val, intbv): if s._init: @@ -507,6 +506,9 @@ def _writeSigDecls(f, intf, siglist, memlist): else: print("constant %s: %s%s := %s;" % (s._name, p, r, "'1'" if s._init else "'0'"), file=f) else: + # 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 ) diff --git a/myhdl/conversion/_toVerilog.py b/myhdl/conversion/_toVerilog.py index 9b76b6df..fcac4de6 100644 --- a/myhdl/conversion/_toVerilog.py +++ b/myhdl/conversion/_toVerilog.py @@ -288,11 +288,6 @@ def _writeModuleHeader(f, intf, doc): r = _getRangeString(s) p = _getSignString(s) if s._driven: - if s._read: - if not isinstance(s, _TristateSignal): - warnings.warn("%s: %s" % (_error.OutputPortRead, portname), - category=ToVerilogWarning - ) if isinstance(s, _TristateSignal): print("inout %s%s%s;" % (p, r, portname), file=f) else: @@ -315,8 +310,14 @@ def _writeSigDecls(f, intf, siglist, memlist): for s in siglist: if not s._used: continue + if s._name in intf.argnames: continue + + if s._name.startswith('-- OpenPort'): + # do not write a signal declaration + continue + r = _getRangeString(s) p = _getSignString(s) if s._driven: @@ -400,14 +401,22 @@ def _writeSigDecls(f, intf, siglist, memlist): '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('localparam {} {} {} [0:{}-1] = {{{}}};'.format(p, r, m.name, m.depth, ', '.join(vals)), file=f) + # remember for SystemVerilog, later + # # 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('localparam {} {} {} [0:{}-1] = \'{{{}}};'.format(p, r, m.name, m.depth, ', '.join(vals)), file=f) + print('reg {}{} {} [0:{}-1];'.format(p, r, m.name, m.depth), file=f) + val_assignments = '\n'.join( + [' %s[%d] <= %s;' % + (m.name, n, _intRepr(each._init)) + for n, each in enumerate(m.mem)]) + initial_assignments = ( + 'initial begin\n' + val_assignments + '\nend') if initial_assignments is not None: print(initial_assignments, file=f) @@ -1237,7 +1246,11 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): raise AssertionError("name ref: %s" % n) if addSignBit: self.write("$signed({1'b0, ") - self.write(s) + + if s.startswith('--'): + self.write(s.replace('--', '//')) + else: + self.write(s) if addSignBit: self.write("})") @@ -1488,6 +1501,12 @@ class _ConvertSimpleAlwaysCombVisitor(_ConvertVisitor): def visit_Attribute(self, node): if isinstance(node.ctx, ast.Store): + # try intercepting '-- OpenPort' signals + if isinstance(node.value, ast.Name): + obj = self.tree.symdict[node.value.id] + if obj._name.startswith('-- OpenPort'): + self.write('// ') + self.write("assign ") self.visit(node.value) else: diff --git a/myhdl/test/conversion/general/test_Constant.py b/myhdl/test/conversion/general/test_Constant.py new file mode 100644 index 00000000..55ec8d61 --- /dev/null +++ b/myhdl/test/conversion/general/test_Constant.py @@ -0,0 +1,167 @@ +''' +Created on 19 okt. 2023 + +@author: josy +''' +from myhdl import (block, Signal, intbv, always_comb, always_seq , Constant, + instances) + + +@block +def scramble(Pattern, A, Y): + NBR_BITS = len(Pattern) + + @always_comb + def dsc(): + for i in range(NBR_BITS): + if Pattern[i]: + Y.next[i] = not A[i] + else: + Y.next[i] = A[i] + + return instances() + + +@block +def contrived(A, Y): + WIDTH_D = len(A) + PAT1 = Constant(intbv(0x42)[WIDTH_D:]) + PAT2 = Constant(intbv(0xbd)[WIDTH_D:]) + y1a2 = Signal(intbv(0)[WIDTH_D:]) + + s1 = scramble(PAT1, A, y1a2) + s2 = scramble(PAT2, y1a2, Y) + + return instances() + + +@block +def contrived2(A, Y): + WIDTH_D = len(A) + PAT = [Constant(intbv(0x42)[WIDTH_D:]), Constant(intbv(0xbd)[WIDTH_D:])] + y1a2 = Signal(intbv(0)[WIDTH_D:]) + + s1 = scramble(PAT[0], A, y1a2) + s2 = scramble(PAT[1], y1a2, Y) + + return instances() + + +@block +def contrived3(WIDTH_D, Sel, Y): + import random + random.seed('We want repeatable randomness') + + A = [Constant(intbv(random.randint(1, 2 ** WIDTH_D - 1))[WIDTH_D:]) for __ in range(8)] + + @always_comb + def cmux(): + Y.next = A[Sel] + + return instances() + + +@block +def contrived4(Clk, D, CE, Q): + + @always_seq(Clk.posedge, reset=None) + def dff(): + if CE: + Q.next = D + + return instances() + + +@block +def wrappercontrived4(Clk, D, Q): + return contrived4(Clk, D, Constant(bool(1)), Q) + + +def test_contrived(): + WIDTH_D = 8 + A, Y = [Signal(intbv(0)[WIDTH_D:]) for __ in range(2)] + assert contrived(A, Y).analyze_convert() == 0 + + +def test_contrived2(): + WIDTH_D = 8 + A, Y = [Signal(intbv(0)[WIDTH_D:]) for __ in range(2)] + assert contrived2(A, Y).analyze_convert() == 0 + + +def test_contrived3(): + WIDTH_D = 8 + Y = Signal(intbv(0)[WIDTH_D:]) + Sel = Signal(intbv(0)[3:]) + assert contrived3(8, Sel, Y).analyze_convert() == 0 + + +def test_contrived4(): + Clk, D , Q = [Signal(bool(0)) for __ in range(3)] + CE = Signal(bool(0)) + assert contrived4(Clk, D, CE, Q).analyze_convert() == 0 + + +def test_contrived4b(): + Clk, D , Q = [Signal(bool(0)) for __ in range(3)] + assert wrappercontrived4(Clk, D, Q).analyze_convert() == 0 + + +if __name__ == '__main__': + + from myhdl import delay, instance, StopSimulation + + @block + def tb_contrived(): + WIDTH_D = 8 + A, Y = [Signal(intbv(0)[WIDTH_D:]) for __ in range(2)] + + # dut = contrived(A, Y) + dut2 = contrived2(A, Y) + + @instance + def stimulus(): + A.next = 0x42 + yield delay(10) + A.next = Y + yield delay(10) + assert Y == 0x42 + + raise StopSimulation + + return instances() + + def convert(): + WIDTH_D = 8 + A, Y = [Signal(intbv(0)[WIDTH_D:]) for __ in range(2)] + Sel = Signal(intbv(0)[3:]) + Clk, D , Q = [Signal(bool(0)) for __ in range(3)] + CE = Signal(bool(0)) + + dfc = contrived(A, Y) + dfc.convert(hdl='VHDL') + dfc.convert(hdl='Verilog') + + dfc2 = contrived2(A, Y) + dfc2.convert(hdl='VHDL') + dfc2.convert(hdl='Verilog') + + dfc3 = contrived3(WIDTH_D, Sel, Y) + dfc3.convert(hdl='VHDL') + dfc3.convert(hdl='Verilog') + + dfc4 = contrived4(Clk, D, CE, Q) + dfc4.convert(hdl='VHDL') + dfc4.convert(hdl='Verilog') + + dfc5 = wrappercontrived4(Clk, D, Q) + dfc5.convert(hdl='VHDL', name='contrived4b') + dfc5.convert(hdl='Verilog', name='contrived4b') + + # dft = tb_contrived() + # dft.config_sim(trace=True) + # dft.run_sim() + # print("Simulation passed") + + convert() + diff --git a/myhdl/test/conversion/general/test_OpenPort.py b/myhdl/test/conversion/general/test_OpenPort.py new file mode 100644 index 00000000..e8ee0371 --- /dev/null +++ b/myhdl/test/conversion/general/test_OpenPort.py @@ -0,0 +1,114 @@ +''' +Created on 19 okt. 2023 + +@author: josy +''' + +from myhdl import (block, Signal, intbv, always_comb, always_seq, instances, + OpenPort) + + +@block +def contrived_dff(Clk, D, Q, OE): + + @always_seq(Clk.posedge, reset=None) + def cdff(): + Q.next = D + OE.next = Q and D + + return instances() + + +@block +def contrived_dff2(Clk, D, Q, OE): + + @always_seq(Clk.posedge, reset=None) + def sdff(): + Q.next = D + + @always_comb + def cdff(): + OE.next = Q and D + + return instances() + + +@block +def wrapper(Clk, D, Q): + return contrived_dff(Clk, D, Q, OE=OpenPort()) + + +@block +def wrapper2(Clk, D, Q): + return contrived_dff2(Clk, D, Q, OE=OpenPort()) + + +def test_contrived_dff2(): + Clk = Signal(bool(0)) + D = Signal(bool(0)) + Q = Signal(bool(0)) + OE = Signal(bool(0)) + + assert contrived_dff(Clk, D, Q, OE).analyze_convert() == 0 + + +def test_wrapper(): + Clk = Signal(bool(0)) + D = Signal(bool(0)) + Q = Signal(bool(0)) + + assert wrapper(Clk, D, Q).analyze_convert() == 0 + + +def test_wrapper2(): + Clk = Signal(bool(0)) + D = Signal(bool(0)) + Q = Signal(bool(0)) + + assert wrapper2(Clk, D, Q).analyze_convert() == 0 + + +if __name__ == '__main__': + + def convert(): + Clk = Signal(bool(0)) + D = Signal(bool(0)) + Q = Signal(bool(0)) + OE = Signal(bool(0)) + + dfc = contrived_dff(Clk, D, Q, OE) + dfc.convert(hdl='VHDL') + dfc.convert(hdl='Verilog') + + dfc2 = wrapper(Clk, D, Q) + dfc2.convert(hdl='VHDL') + dfc2.convert(hdl='Verilog') + + dfc3 = wrapper2(Clk, D, Q) + dfc3.convert(hdl='VHDL') + dfc3.convert(hdl='Verilog') + + convert() + + @block + def dff(Clk, D, Q, Q_n): + + @always_seq(Clk.posedge, reset=None) + def sdff(): + Q.next = D + Q_n.next = not D + + return instances() + + @block + def wrapper_dff(Clk, D, Q): + return dff(Clk, D, Q, Q_n=OpenPort()) + + Clk = Signal(bool(0)) + D = Signal(bool(0)) + Q = Signal(bool(0)) + + dfc = wrapper_dff(Clk, D, Q) + dfc.convert(hdl='Verilog') + dfc.convert(hdl='VHDL') + diff --git a/myhdl/test/conversion/general/test_binops.py b/myhdl/test/conversion/general/test_binops.py index 4eab0042..758a5c0f 100644 --- a/myhdl/test/conversion/general/test_binops.py +++ b/myhdl/test/conversion/general/test_binops.py @@ -460,3 +460,11 @@ def test_binOps2_convert(): assert i_dut.analyze_convert() == 0 # assert i_dut.verify_convert() == 0 # assert i_dut.verify_simulator() == 0 + + +if __name__ == '__main__': + a = Signal(intbv(0)[3:]) + z = Signal(intbv(0)[3:]) + + i_dut = binOpsCheck2(a, z) + i_dut.convert(hdl='Verilog')