Source code for pybert.cdr

"""
Behavioral model of a "bang-bang" clock data recovery (CDR) unit.

Original Author: David Banas <capn.freako@gmail.com>

Original Date:   17 June 2014

This Python script provides a behavioral model of a "bang-bang" clock
data recovery (CDR) unit. The class defined, here, is intended for
integration into the larger 'PyBERT' framework.

Copyright (c) 2014 by David Banas; All rights reserved World wide.
"""

from numpy import sign, array, arange, mean, where

[docs]class CDR(object): """ A class providing behavioral modeling of a 'bang- bang' clock data recovery (CDR) unit. """ def __init__(self, delta_t, alpha, ui, n_lock_ave=500, rel_lock_tol=0.01, lock_sustain=500): """ Inputs: Required: - delta_t the proportional branch correction (s) - alpha the integral branch correction (norm.) (Normalized to proportional branch correction.) - ui the nominal unit interval (s) Optional: - n_lock_ave Number of unit intervals to use for determining lock. - rel_lock_tol Lock tolerance, relative to 'delta_t'. - lock_sustain Length of lock sustain vector used to provide histerysis. Note that the code does not care what units are actually used for 'delta_t' and 'ui'; only that they are the same. """ self.delta_t = delta_t self.alpha = alpha self.nom_ui = ui self._ui = ui self.n_lock_ave = n_lock_ave self.rel_lock_tol = rel_lock_tol self._locked = False self.lock_sustain = lock_sustain self.integral_corrections = [0.0] self.proportional_corrections = [] self.lockeds = [] @property def ui(self): """The current unit interval estimate.""" return self._ui @property def locked(self): """The current locked state.""" return self._locked
[docs] def adapt(self, samples): """ Adapt period/phase, according to 3 samples. Synopsis: (ui, locked) = adapt(samples) Should be called, when the clock has just struck. Inputs: - samples a list of 3 samples of the input waveform: - at the last clock time - at the last unit interval boundary time - at the current clock time Outputs: - ui the new unit interval estimate - locked Boolean flag indicating 'locked' status """ integral_corrections = self.integral_corrections proportional_corrections = self.proportional_corrections delta_t = self.delta_t locked = self._locked lockeds = self.lockeds lock_sustain = self.lock_sustain n_lock_ave = self.n_lock_ave rel_lock_tol = self.rel_lock_tol integral_correction = integral_corrections[-1] samples = map(sign, samples) if(samples[0] == samples[2]): # No transition; no correction. proportional_correction = 0.0 elif(samples[0] == samples[1]): # Early clock; increase period. proportional_correction = delta_t else: # Late clock; decrease period. proportional_correction = -delta_t integral_correction += self.alpha * proportional_correction ui = self.nom_ui + integral_correction + proportional_correction integral_corrections.append(integral_correction) if(len(integral_corrections) > n_lock_ave): integral_corrections.pop(0) proportional_corrections.append(proportional_correction) if(len(proportional_corrections) > n_lock_ave): proportional_corrections.pop(0) if(len(proportional_corrections) == n_lock_ave): x = array(integral_corrections) #- mean(integral_corrections) var = sum(x ** 2) / n_lock_ave lock = abs(mean(proportional_corrections) / delta_t) < rel_lock_tol and (var / delta_t) < rel_lock_tol lockeds.append(lock) if(len(lockeds) > lock_sustain): lockeds.pop(0) if(locked): if(len(where(array(lockeds) == True)[0]) < 0.2 * lock_sustain): locked = False else: if(len(where(array(lockeds) == True)[0]) > 0.8 * lock_sustain): locked = True self._locked = locked self.integral_corrections = integral_corrections self.proportional_corrections = proportional_corrections self.lockeds = lockeds self._ui = ui return (ui, locked)