1
0
mirror of https://github.com/myhdl/myhdl.git synced 2024-12-14 07:44:38 +08:00

**OpenPort** for unused outputs (#424)

* Added `OpenPort` to emulate VHDL 'open' port

* Of course we need the new module: _openport.py

* refining detection of OpenPrt in _toVerilog.py

* Corrected constant array for Verilog 2005
This commit is contained in:
Josy Boelen 2023-10-19 20:54:26 +02:00 committed by GitHub
parent 15104a0f8c
commit 7cef85c1c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 400 additions and 23 deletions

View File

@ -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"
]

65
myhdl/_openport.py Normal file
View File

@ -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

View File

@ -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
)

View File

@ -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:

View File

@ -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()

View File

@ -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')

View File

@ -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')