Source code for hwtLib.clocking.timers

from typing import Optional, Union

from hwt.code import If
from hwt.hdl.types.bits import Bits
from hwt.hdl.value import HValue
from hwt.math import log2ceil, isPow2
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
from hwt.synthesizer.unit import Unit


[docs]class TimerInfo(object): """ Generator of varius shared timers. Use this from :class:`hwtLib.clocking.clkBuilder.ClkBuilder` :ivar ~.cntrRegister: counter register for this timer :ivar ~.tick: signal with tick from this timer :ivar ~.parent: parent TimerInfo object from which this timer can be generated :ivar ~.maxValOriginal: original value of maxVal :ivar ~.maxVal: evaluated value of maxVal :ivar ~.name: name prefix which is used for registers and signals for this timer """ __slots__ = ['maxVal', 'maxValOriginal', 'parent', 'cntrRegister', 'tick', 'name']
[docs] def __init__(self, maxVal, name=None): self.maxValOriginal = maxVal self.maxVal = int(maxVal) assert isinstance(self.maxVal, int), self.maxVal self.parent = None if name is None: self.name = "" else: self.name = name
[docs] @staticmethod def resolveSharing(timers): # filter out timers with maxVal == 1 because they are only clock timers = sorted((t for t in timers if t.maxVal != 1), key=lambda t: t.maxVal, reverse=True) for i, t in enumerate(timers): if isPow2(t.maxVal): # this timer will be driven from bit of some larger # counter if there is suitable for possibleParent in timers[:i]: if possibleParent.maxVal % t.maxVal == 0: if possibleParent.parent is not None: continue t.parent = possibleParent break else: # this timer will have its own timer which will be enabled by # some other timer if there is suitable for possibleParent in timers[(i + 1):]: if t.maxVal % possibleParent.maxVal == 0: if possibleParent.parent is t: continue t.parent = possibleParent break
[docs] @staticmethod def _instantiateTimerWithParent(parentUnit, timer, parent, enableSig, rstSig): p = parent if not hasattr(p, "tick"): TimerInfo._instantiateTimer(parentUnit, p, enableSig, rstSig) assert hasattr(p, "tick") if p.maxVal == timer.maxVal: timer.cntrRegister = p.cntrRegister timer.tick = p.tick elif p.maxVal < timer.maxVal: maxVal = (timer.maxVal // p.maxVal) - 1 assert maxVal >= 0 timer.tick = parentUnit._sig( f"{timer.name:s}timerTick{timer.maxVal:d}") timer.cntrRegister = parentUnit._reg( f"{timer.name:s}timerCntr{timer.maxVal:d}", Bits(log2ceil(maxVal + 1)), maxVal ) en = p.tick if enableSig is not None: en = en & enableSig tick = TimerInfo._instantiateTimerTickLogic( parentUnit, timer, (timer.maxValOriginal // p.maxValOriginal) - 1, en, rstSig) timer.tick(tick & p.tick) else: # take specific bit from wider counter assert isPow2(timer.maxVal), timer.maxVal bitIndx = log2ceil(timer.maxVal) timer.cntrRegister = p.cntrRegister timer.tick = p.cntrRegister[bitIndx:]._eq(0) if enableSig is not None: timer.tick = timer.tick & enableSig
[docs] @staticmethod def _instantiateTimerTickLogic(parentUnit: Unit, timer: RtlSignal, origMaxVal: Union[int, RtlSignal, HValue], enableSig: Optional[RtlSignal], rstSig: Optional[RtlSignal]) -> RtlSignal: """ Instantiate logic of this timer :return: tick signal from this timer """ r = timer.cntrRegister tick = r._eq(0) if enableSig is None: if rstSig is None: If(tick, r(origMaxVal) ).Else( r(r - 1) ) else: If(rstSig | tick, r(origMaxVal) ).Else( r(r - 1) ) else: if rstSig is None: If(enableSig, If(tick, r(origMaxVal) ).Else( r(r - 1) ) ) else: If(rstSig | (enableSig & tick), r(origMaxVal) ).Elif(enableSig, r(r - 1) ) if enableSig is not None: tick = (tick & enableSig) if rstSig is not None: tick = (tick & ~rstSig) if timer.name: # wrap tick in signal s = parentUnit._sig(timer.name) s(tick) tick = s return tick
[docs] @staticmethod def _instantiateTimer(parentUnit, timer, enableSig=None, rstSig=None): """ :param enableSig: enable signal for all counters :param rstSig: reset signal for all counters """ if timer.parent is None: maxVal = timer.maxVal - 1 # use original to propagate parameter origMaxVal = timer.maxValOriginal - 1 assert maxVal >= 0 if maxVal == 0: if enableSig is None: tick = 1 else: tick = enableSig else: timer.cntrRegister = parentUnit._reg( f"{timer.name:s}timerCntr{timer.maxVal:d}", Bits(log2ceil(maxVal + 1)), maxVal ) tick = TimerInfo._instantiateTimerTickLogic(parentUnit, timer, origMaxVal, enableSig, rstSig) timer.tick = parentUnit._sig( f"{timer.name:s}timerTick{timer.maxVal:d}", ) timer.tick(tick) else: TimerInfo._instantiateTimerWithParent( parentUnit, timer, timer.parent, enableSig, rstSig)
[docs] @staticmethod def instantiate(parentUnit, timers, enableSig=None, rstSig=None): """ :param enableSig: enable signal for all counters :param rstSig: reset signal for all counters """ for timer in timers: if not hasattr(timer, "tick"): TimerInfo._instantiateTimer(parentUnit, timer, enableSig=enableSig, rstSig=rstSig)
[docs] def __repr__(self): return f"<{self.__class__.__name__:s} maxVal={self.maxVal:d}>"
[docs]class DynamicTimerInfo(TimerInfo): """ Meta informations about timer with dynamic period :note: See :class:`.TimerInfo` """
[docs] def __init__(self, maxVal, name=None): self.maxValOriginal = maxVal self.maxVal = maxVal self.parent = None if name is None: self.name = "" else: self.name = name
[docs] @staticmethod def _instantiateTimerTickLogic(timer: RtlSignal, period: RtlSignal, enableSig: Optional[RtlSignal], rstSig: Optional[RtlSignal]): """ Instantiate incrementing timer with optional reset and enable signal :param timer: timer main register :param period: signal with actual period :param enableSig: optional enable signal for this timer :param rstSig: optional reset signal for this timer """ r = timer.cntrRegister tick = r._eq(period - 1) if enableSig is None: if rstSig is None: cond = tick else: cond = rstSig | tick, If(cond, r(0) ).Else( r(r + 0) ) else: if rstSig is None: If(enableSig, If(tick, r(0) ).Else( r(r + 1) ) ) else: If(rstSig | (enableSig & tick), r(0) ).Elif(enableSig, r(r + 1) ) if enableSig is not None: tick = (tick & enableSig) if rstSig is not None: tick = (tick & ~rstSig) return tick