from hwt.code import If, log2ceil
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.defs import BIT
from hwt.synthesizer.interfaceLevel.unitImplHelpers import getSignalName
from hwtLib.clocking.timers import TimerInfo, DynamicTimerInfo
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
from typing import Tuple
[docs]class ClkBuilder(object):
"""
:ivar compId: last component id used to avoid name collisions
:ivar parent: unit in which will be all units created by this builder instantiated
:ivar name: prefix for all instantiated units
:ivar end: interface where builder ended
"""
[docs] def __init__(self, parent, srcInterface, name=None):
"""
:param parent: unit in which will be all units created by this builder instantiated
:param name: prefix for all instantiated units
:param srcInterface: input clock
"""
self.parent = parent
self.end = srcInterface
if name is None:
name = "gen_" + getSignalName(srcInterface)
self.name = name
self.compId = 0
[docs] def timers(self, periods, enableSig=None, rstSig=None):
"""
generate counters specified by count of iterations
:param periods: list of integers/params which specifies periods of timers
or tuple (name, integer/param)
:param enableSig: enable signal for all counters
:param rstSig: reset signal for all counters
:attention: if tick of timer his high and enableSig falls low tick will stay high
:return: list of tick signals from timers
"""
timers = []
for p in periods:
if isinstance(p, (tuple, list)):
name, period = p
else:
name = None
period = p
t = TimerInfo(period, name=name)
timers.append(t)
TimerInfo.resolveSharing(timers)
TimerInfo.instantiate(self.parent, timers, enableSig=enableSig, rstSig=rstSig)
return list(map(lambda timer: timer.tick, timers))
[docs] def timer(self, period, enableSig=None, rstSig=None):
"""
Same as timers, just for one
"""
return self.timers([period, ], enableSig=enableSig, rstSig=rstSig)[0]
[docs] def timerDynamic(self, periodSig, enableSig=None, rstSig=None) -> RtlSignal:
"""
Same as timer, just period is signal which can be configured dynamically
"""
if isinstance(periodSig, (tuple, list)):
periodSig, name = periodSig
else:
periodSig, name = periodSig, getSignalName(periodSig)
parentUnit = self.parent
timer = DynamicTimerInfo(periodSig, name)
maxVal = timer.maxVal - 1
assert maxVal._dtype.bit_length() > 0
r = parentUnit._reg(timer.name + "_delayCntr",
periodSig._dtype,
defVal=0
)
timer.cntrRegister = r
tick = DynamicTimerInfo._instantiateTimerTickLogic(timer,
periodSig,
enableSig,
rstSig)
timer.tick = parentUnit._sig(timer.name + "_delayTick")
timer.tick(tick)
return timer.tick
[docs] def oversample(self, sig, sampleCount, sampleTick, rstSig=None) -> Tuple[RtlSignal, RtlSignal]:
"""
[TODO] last sample is not sampled correctly
:param sig: signal to oversample
:param sampleCount: count of samples to do
:param sampleTick: signal to enable next sample taking
:param rstSig: rstSig signal to reset internal counter, if is None it is not used
:return: typle (oversampled signal, oversample valid signal)
"""
if sig._dtype != BIT:
raise NotImplementedError()
n = getSignalName(sig)
sCnt = int(sampleCount)
sampleDoneTick = self.timer((n + "_oversampleDoneTick", sampleCount),
enableSig=sampleTick,
rstSig=rstSig)
oversampleCntr = self.parent._reg(n + "_oversample%d_cntr" % (sCnt),
Bits(log2ceil(sampleCount) + 1, False),
defVal=0)
if rstSig is None:
rstSig = sampleDoneTick
else:
rstSig = rstSig | sampleDoneTick
If(sampleDoneTick,
oversampleCntr(0)
).Elif(sampleTick & sig,
oversampleCntr(oversampleCntr + 1)
)
oversampled = self.parent._sig(n + "_oversampled%d" % (sCnt))
oversampled(oversampleCntr > (sampleCount // 2 - 1))
return oversampled, sampleDoneTick
[docs] def edgeDetector(self, sig, rise=False, fall=False, last=None, initVal=0):
"""
:param sig: signal to detect edges on
:param rise: if True signal for rise detecting will be returned
:param fall: if True signal for fall detecting will be returned
:param last: last value for sig (use f.e. when you have register and it's next signal (sig=reg.next, last=reg))
if last is None last register will be automatically generated
:param initVal: if last is None initVal will be used as its initialization value
:return: signals which is high on on rising/falling edge or both (specified by rise, fall parameter)
"""
namePrefix = getSignalName(sig)
assert rise or fall
if last is None:
last = self.parent._reg(namePrefix + "_edgeDetect_last", defVal=initVal)
last(sig)
if rise:
riseSig = self.parent._sig(namePrefix + "_rising")
riseSig(sig & ~last)
if fall:
fallSig = self.parent._sig(namePrefix + "_falling")
fallSig(~sig & last)
if rise and not fall:
return riseSig
elif not rise and fall:
return fallSig
else:
return (riseSig, fallSig)