Source code for hwtLib.clocking.clkBuilder

from typing import Tuple, Union, List

from hwt.code import If
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.defs import BIT
from hwt.math import log2ceil
from hwt.synthesizer.interface import Interface
from hwt.synthesizer.interfaceLevel.unitImplHelpers import getSignalName
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
from hwtLib.abstract.componentBuilder import AbstractComponentBuilder
from hwtLib.clocking.timers import TimerInfo, DynamicTimerInfo


AnySig = Union[RtlSignal, Interface]


[docs]class ClkBuilder(AbstractComponentBuilder): """ Helper object which simplifies construction of the oversampling, shared timers, edge detector, ... logic :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 timers(self, periods: List[Union[int, Tuple[str, int]]], enableSig=None, rstSig=None): """ generate counters specified by count of iterations :param periods: list tick period or tuple (name, period) for timer, period is specified in the number of clk ticks :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 [timer.tick for timer in timers]
[docs] def timer(self, period, enableSig=None, rstSig=None): """ Same as :func:`.ClkBuilder.timers`, just for single timer intance """ 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, def_val=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(f"{n:s}_oversample{sCnt:d}_cntr", Bits(log2ceil(sampleCount) + 1, False), def_val=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(f"{n:s}_oversampled{sCnt:d}") 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 e.g. 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", def_val=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)
[docs] def reg_path(self, din, number_of_regs, name=None, def_val=None): """ Instantiate path of registers used to delay the signal or to filter IO :return: the last register in path """ if name is None: name = "reg_path" for i in range(number_of_regs): reg = self.parent._reg(f"{name:s}_{i:d}", dtype=din._dtype, def_val=def_val) reg(din) din = reg return din