For example, in the below code, the “clock_tick.py” instantiate the “mod_m_counter.py”; and the output port ‘count’ is not used. Therefore a warning is generated for this “Signal is driven but not read: count”
That’s what the OP has,count is declared but not read and subsequently one gets the warning he wants to get rid off.
Issue 126: I have since then adapted my MyHDL code to accept a SuppressWarning for a Signal. At the end of the conversion I post-edit the VHDL file and remove all lines for the suppressed Signals.
Better is to not declare unused signals. I routinely use the ‘Open’ to tell the callee that I don’t need the output. Of course this has to be handled by the called module. In case of the OP’s example the module mod_m_counter needs adapting to handle this.
I switched to Open as in VHDL as I use None to specify that I want my class-based code to declare the signal internally.
I code my MyHDL modules as classes and use None to indicate that the __init__() must allocate the Signal for the Port itself. ``‘Open’``` indicates that I am not going to use that specific output port so I will take care that no code is generated keeping the warnings away both here in MyHDL as later when compiling the generated code with the Vendor’s tools.
I made a gist a couple of years back: https://gist.github.com/josyb/afd84c9a06fdec77f2fd
I don’t have small true example ready but this is a counter I use all the time:
class UpCounter(object):
''' a 'generic' upcounter class '''
def __init__(self, RANGE_COUNT,
Clk, Reset,
SClr=None, CntEn=None, Max=None,
Q=None, IsMaxMinusOne=None, IsMax=None,
WRAP_AROUND=False,
IsZero=None
):
''' RANGE_COUNT
Clk, Reset: self-explanatory
SClr: resets the counter to zero, overrides CntEn
CntEn: (possibly) advances count by one
Max: value at which either the counter tops out or wraps around,
can be either a Signal or an integer number
Not that Max must be kept valid, and may only change during SClr active
Q: the actual count output
IsMaxMinusOne, IsMax: self-explanatory
WRAP_AROUND: self-explanatory
'''
self.range_count = RANGE_COUNT
self.wrap_around = WRAP_AROUND
self.width_counter = hdlutils.widthu(self.range_count)
self.clk = Clk
self.reset = Reset
self.SClr = SClr if SClr is not None else Signal(bool(0))
self.CntEn = CntEn if CntEn is not None else Signal(bool(1))
if Max is None:
self.Max = RANGE_COUNT - 1
else:
self.Max = Max
if isinstance(self.Max, int):
assert self.Max > 0, 'Not much use counting to 1'
self.Q = Q if Q is not None else Signal(intbv(0)[self.width_counter:])
self.IsMaxMinusOne = IsMaxMinusOne if IsMaxMinusOne is not None else Signal(bool(0))
self.IsMax = IsMax if IsMax is not None else Signal(bool(0))
self.IsZero = IsZero if IsZero is not None else Signal(bool(0))
def rtl(self):
''' the upcounter rtl '''
# local Signals
counter = Signal(intbv(0)[self.width_counter:])
limmo = Signal(bool(self.range_count == 2))
lim = Signal(bool(0))
liz = Signal(bool(1))
if isinstance(self.Max, int):
if self.Max == 1:
# actually counting 2 steps
# 0: 1 1 0
# 1: 0 0 1
if self.wrap_around:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounterwr():
''' UpCounter: wrap around '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
liz.next = 1
limmo.next = 1
lim.next = 0
else:
# CntEn
if lim:
counter.next = 0
liz.next = 1
lim.next = 0
limmo.next = 1
else:
counter.next = 1
liz.next = 0
limmo.next = 0
lim.next = 1
else:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounteros():
''' UpCounter: one-shot '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
limmo.next = 1
lim.next = 0
liz.next = 1
else:
counter.next = 1
liz.next = 0
limmo.next = 0
lim.next = 1
elif self.IsMax == 2:
# actually counting 3 steps
# 0: 1 0 0
# 1: 0 1 0
# 2: 0 0 1
if self.wrap_around:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounterwr():
''' UpCounter: wrap around '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
liz.next = 1
limmo.next = 0
lim.next = 0
else:
# CntEn
if lim:
# wrap around
counter.next = 0
liz.next = 1
lim.next = 0
limmo.next = 0
else:
liz.next = 0
counter.next = counter + 1
if limmo:
limmo.next = 0
lim.next = 1
else:
limmo.next = 1
else:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounteros():
''' UpCounter: one-shot '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
limmo.next = 0
lim.next = 0
liz.next = 1
else:
liz.next = 0
if not lim:
counter.next = counter + 1
if limmo:
limmo.next = 0
lim.next = 1
else:
limmo.next = 1
else:
# actually counting N steps with N >= 4
# 0: 1 0 0
# 1: 0 0 0
# ...
# N - 2: 0 1 0
# N - 1: 0 0 1
if self.wrap_around:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounterwr():
''' UpCounter: wrap around '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
liz.next = 1
limmo.next = 0
lim.next = 0
else:
# CntEn
if lim:
# wrap around
counter.next = 0
liz.next = 1
lim.next = 0
limmo.next = 0
else:
liz.next = 0
counter.next = counter + 1
if limmo:
limmo.next = 0
lim.next = 1
else:
if counter == (self.Max - 2):
limmo.next = 1
else:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounteros():
''' UpCounter: one-shot '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
limmo.next = 0
lim.next = 0
liz.next = 1
else:
liz.next = 0
if not lim:
counter.next = counter + 1
if limmo:
limmo.next = 0
lim.next = 1
else:
if counter == self.Max - 2:
limmo.next = 1
else:
# self.Max is a dynamic Signal
if self.wrap_around:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounterwr():
''' UpCounter: wrap around '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
liz.next = 1
limmo.next = 0 if self.Max > 1 else 1
lim.next = 0 if self.Max > 0 else 1
else:
# CntEn
if liz:
if self.Max > 0:
counter.next = 1
liz.next = 0
if self.Max == 1:
limmo.next = 0
lim.next = 1
else:
if self.Max == 2:
limmo.next = 1
elif lim:
counter.next = 0
liz.next = 1
if self.Max > 0:
lim.next = 0
else:
lim.next = 1
if self.Max > 1:
limmo.next = 0
else:
limmo.next = 1
else:
counter.next = counter + 1
if limmo:
limmo.next = 0
lim.next = 1
elif self.Max > 1 and counter == (self.Max - 2):
limmo.next = 1
else:
@always_seq(self.clk.posedge, reset=self.reset)
def upcounteros():
''' UpCounter: one-shot '''
if self.SClr or self.CntEn:
if self.SClr:
counter.next = 0
limmo.next = 0 if self.Max > 1 else 1
lim.next = 0 if self.Max > 0 else 1
liz.next = 1
else:
liz.next = 0
if not lim:
counter.next = counter + 1
if counter == self.Max - 2:
limmo.next = 1
if limmo:
limmo.next = 0
lim.next = 1
if self.Q != 'Open':
@always_comb
def assignQ():
self.Q.next = counter
if self.IsMaxMinusOne != 'Open':
@always_comb
def assignimmo():
self.IsMaxMinusOne.next = limmo
else:
limmo.suppressWarning()
if self.IsMax != 'Open':
@always_comb
def assignim():
self.IsMax.next = lim
if self.IsZero != 'Open':
@always_comb
def assigniz():
self.IsZero.next = liz
else:
liz.suppressWarning()
return instances()
The advantage of this coding style is that you can build a structural design with little lines of codes with few (even none) cluttering glue Signals. E.g:
class UART(object):
'''
a simple Universal Asynchronous Receiver Transmitter
here we implement the minimal Transmit/Receive functionality
'''
def __init__(self, SYSFREQ_in_Hz,
BAUDRATE_in_Bd,
Clk, Reset,
Sink=None, Source=None,
TxD=None, RxD=None,
NBROFSTOPBITS=1, PARITY=None,
):
self.SYSFREQ_in_Hz = SYSFREQ_in_Hz
self.BAUDRATE_in_Bd = BAUDRATE_in_Bd
self.NBROFSTOPBITS = NBROFSTOPBITS
assert PARITY in (None, 'Mark', 'Space', 'Odd', 'Even')
self.PARITY = PARITY
self.Clk = Clk
self.Reset = Reset
self.Sink = Sink if Sink is not None else SinkSource(8)
self.Source = Source if Source is not None else SinkSource(8)
self.TxD = TxD if TxD is not None else Signal(bool(0))
self.RxD = RxD if RxD is not None else Signal(bool(0))
def rtl(self):
''' the logic '''
tx = UartTx(self.SYSFREQ_in_Hz, self.BAUDRATE_in_Bd, self.Clk, self.Reset,
self.Sink,
self.TxD,
NBROFSTOPBITS=self.NBROFSTOPBITS, PARITY=self.PARITY
)
txrtl = tx.rtl()
metarx = Meta(self.Clk, self.RxD)
metarxrtl = metarx.rtl()
rx = UartRx(self.SYSFREQ_in_Hz, self.BAUDRATE_in_Bd, self.Clk, self.Reset,
metarx.SigOut, self.Source, NBROFSTOPBITS=self.NBROFSTOPBITS, PARITY=self.PARITY)
rxrtl = rx.rtl()
return txrtl, metarxrtl, rxrtl
That’s interesting. However, I think we should not have to care about opened signals in the source code.
Unrelated to the open problem : On my side, I use class parameters for component configuration and rtl() parameters for component signals. This has the advantage of clearly identifying parameters and signals.
Please explain; instantiated Signals that are driven but not read (==opened) will generate a warning everywhere, and some of us like to get rid of these (unnecessary) warnings.
What I mean is that MyHDL should get rid of the opened signal automatically by using a special syntax at module instantiation (Open() for example).
For example :
Yes, that would be nice. I thought about that at the time, but probably was in a hurry, but even worse, I’m not that good a programmer, at least I didn’t see an easy solution. Perhaps one of these days with MyHDL 2.0 we can work this out? Or have you already?
I understand your class approach. IMHO it doesn’t bring as much as mine as in a large module you will have to declare many glue variables to connect all sub-modules where in my case these are delegated to the sub-modules itself. An example:
Yes, one could do the ports in the rtl() method, but my main goal is to have the class modules declare their output ports (as much as possible) and possibly also some input ports and this is done in the __init__() call. PyLint will issue a warning if you add an attribute outside the __init__() call, see here: python - why is defining an object variable outside of __init__ frowned upon? - Stack Overflow
There is one more reason: I wanted to automate the collection as done with instances(). This necessitates that the call to the rtl() method has no arguments. Alas I got stuck in deeper Python too
I spent few hours on it last week.
I’ve ended up with a working solution. I now have 2 projects without warnings .
I still have to work on it to cover more cases.
Everything is based on :
Minor modifications to _Signal class
Slight modifications to _toVHDL module
A new NotUsed() function.
NotUsed can be used in 2 ways :
Without argument directly in the port list of the instantiation of a module (like open in VHDL)
With a signal as argument. This allows to tell a signal of an interface is not used.
The current implementation comments out the signal definition and every line where the not used signal is modified. This way, you can easily check the effects of NotUsed().
What would be really cool is to strip all unused logic following the removal of unused ports. But this is another story.
I have done about exactly that, except the NotUsed() approach, I have modified both _signal.py and _tovhdl.py to accommodate a SuppresWarning method. I post-edit the generated VHDL code and delete the lines for suppressed signals (ruecksichtlos). I have discussed this in issue #126
Can I see your NotUsed() code and an example?