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:
parent
15104a0f8c
commit
7cef85c1c6
@ -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
65
myhdl/_openport.py
Normal 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
|
@ -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
|
||||
)
|
||||
|
@ -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:
|
||||
|
167
myhdl/test/conversion/general/test_Constant.py
Normal file
167
myhdl/test/conversion/general/test_Constant.py
Normal 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()
|
||||
|
114
myhdl/test/conversion/general/test_OpenPort.py
Normal file
114
myhdl/test/conversion/general/test_OpenPort.py
Normal 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')
|
||||
|
@ -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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user