#! /usr/bin/env python
"""
Bit error rate tester (BERT) simulator, written in Python.
Original Author: David Banas <capn.freako@gmail.com>
Original Date: 17 June 2014
Testing by: Mark Marlett <mark.marlett@gmail.com>
This Python script provides a GUI interface to a BERT simulator, which
can be used to explore the concepts of serial communication link design.
The application source is divided among several files, as follows:
pybert.py - This file. It contains:
- independent variable declarations
- default initialization
- the definitions of those dependent variables, which are handled
automatically by the Traits/UI machinery.
pybert_view.py - Contains the main window layout definition, as
well as the definitions of user invoked actions
(i.e.- buttons).
pybert_cntrl.py - Contains the definitions for those dependent
variables, which are updated not automatically by
the Traits/UI machinery, but rather by explicit
user action (i.e. - button clicks).
pybert_util.py - Contains general purpose utility functionality.
dfe.py - Contains the decision feedback equalizer model.
cdr.py - Contains the clock data recovery unit model.
Copyright (c) 2014 by David Banas; All rights reserved World wide.
"""
from traits.api import HasTraits, Array, Range, Float, Int, Property, String, cached_property, Instance, HTML, List, Bool, File
from chaco.api import Plot, ArrayPlotData, VPlotContainer, GridPlotContainer, ColorMapper, Legend, OverlayPlotContainer, PlotAxis
from chaco.tools.api import PanTool, ZoomTool, LegendTool, TraitsTool, DragZoom
from numpy import array, linspace, zeros, histogram, mean, diff, log10, transpose, shape
from numpy.fft import fft
from numpy.random import randint
from scipy.signal import lfilter, iirfilter
from pybert_view import *
from pybert_cntrl import *
from pybert_util import *
debug = False
# Default model parameters - Modify these to customize the default simulation.
# - Simulation Control
gUI = 100 # (ps)
gNbits = 8000 # number of bits to run
gPatLen = 127 # repeating bit pattern length
gNspb = 32 # samples per bit
# - Channel Control
# - parameters for Howard Johnson's "Metallic Transmission Model"
# - (See "High Speed Signal Propagation", Sec. 3.1.)
# - ToDo: These are the values for 24 guage twisted copper pair; need to add other options.
gRdc = 0.1876 # Ohms/m
gw0 = 10.e6 # 10 MHz is recommended in Ch. 8 of his second book, in which UTP is described in detail.
gR0 = 1.452 # skin-effect resistance (Ohms/m)
gTheta0 = .02 # loss tangent
gZ0 = 100. # characteristic impedance in LC region (Ohms)
gv0 = 0.67 # relative propagation velocity (c)
gl_ch = 1.0 # cable length (m)
gRn = 0.01 # standard deviation of Gaussian random noise (V) (Applied at end of channel, so as to appear white to Rx.)
# - Tx
gVod = 1.0 # output drive strength (Vp)
gRs = 100 # differential source impedance (Ohms)
gCout = 0.50 # parasitic output capacitance (pF) (Assumed to exist at both 'P' and 'N' nodes.)
gPnMag = 0.1 # magnitude of periodic noise (V)
gPnFreq = 0.437 # frequency of periodic noise (MHz)
# - Rx
gRin = 100 # differential input resistance
gCin = 0.50 # parasitic input capacitance (pF) (Assumed to exist at both 'P' and 'N' nodes.)
gCac = 1. # a.c. coupling capacitance (uF) (Assumed to exist at both 'P' and 'N' nodes.)
gBW = 12. # Rx signal path bandwidth, assuming no CTLE action. (GHz)
gUseCtle = True # Include CTLE when running simulation.
gUseDfe = True # Include DFE when running simulation.
gDfeIdeal = True # DFE ideal summing node selector
gPeakFreq = 5. # CTLE peaking frequency (GHz)
gPeakMag = 10. # CTLE peaking magnitude (dB)
# - DFE
gDecisionScaler = 0.5
gNtaps = 5
gGain = 0.1
gNave = 100
gDfeBW = 12. # DFE summing node bandwidth (GHz)
# - CDR
gDeltaT = 0.1 # (ps)
gAlpha = 0.01
gNLockAve = 500 # number of UI used to average CDR locked status.
gRelLockTol = .1 # relative lock tolerance of CDR.
gLockSustain = 500
# - Analysis
gThresh = 6 # threshold for identifying periodic jitter spectral elements (sigma)
[docs]class PyBERT(HasTraits):
"""
A serial communication link bit error rate tester (BERT) simulator with a GUI interface.
Useful for exploring the concepts of serial communication link design.
"""
# Independent variables
# - Simulation Control
ui = Float(gUI) # (ps)
nbits = Int(gNbits)
pattern_len = Int(gPatLen)
nspb = Int(gNspb)
eye_bits = Int(gNbits // 5)
mod_type = List([0])
# - Channel Control
use_ch_file = Bool(False)
ch_file = File('', entries=5, filter=['*.csv'])
Rdc = Float(gRdc)
w0 = Float(gw0)
R0 = Float(gR0)
Theta0 = Float(gTheta0)
Z0 = Float(gZ0)
v0 = Float(gv0)
l_ch = Float(gl_ch)
# - Tx
vod = Float(gVod) # (V)
rs = Float(gRs) # (Ohms)
cout = Float(gCout) # (pF)
pn_mag = Float(gPnMag) # (ps)
pn_freq = Float(gPnFreq) # (MHz)
rn = Float(gRn) # (V)
pretap = Float(-0.05)
posttap = Float(-0.10)
# - Rx
rin = Float(gRin) # (Ohmin)
cin = Float(gCin) # (pF)
cac = Float(gCac) # (uF)
rx_bw = Float(gBW) # (GHz)
use_dfe = Bool(gUseDfe)
sum_ideal = Bool(gDfeIdeal)
peak_freq = Float(gPeakFreq) # CTLE peaking frequency (GHz)
peak_mag = Float(gPeakMag) # CTLE peaking magnitude (dB)
# - DFE
decision_scaler = Float(gDecisionScaler)
gain = Float(gGain)
n_ave = Float(gNave)
n_taps = Int(gNtaps)
sum_bw = Float(gDfeBW) # (GHz)
# - CDR
delta_t = Float(gDeltaT) # (ps)
alpha = Float(gAlpha)
n_lock_ave = Int(gNLockAve)
rel_lock_tol = Float(gRelLockTol)
lock_sustain = Int(gLockSustain)
# - Analysis
thresh = Int(gThresh)
# - Plots (plot containers, actually)
plotdata = ArrayPlotData()
plots_h = Instance(GridPlotContainer)
plots_s = Instance(GridPlotContainer)
plots_H = Instance(GridPlotContainer)
plots_dfe = Instance(GridPlotContainer)
plots_eye = Instance(GridPlotContainer)
plots_jitter_dist = Instance(GridPlotContainer)
plots_jitter_spec = Instance(GridPlotContainer)
plots_bathtub = Instance(GridPlotContainer)
# - Status
status = String("Ready.")
jitter_perf = Float(0.)
total_perf = Float(0.)
# - About
ident = String('PyBERT v1.2 - a serial communication link design tool, written in Python\n\n \
David Banas\n \
February 10, 2015\n\n \
Copyright (c) 2014 David Banas;\n \
All rights reserved World wide.')
# - Help
instructions = Property(HTML)
# Dependent variables
# - Handled by the Traits/UI machinery. (Should only contain "low overhead" variables, which don't freeze the GUI noticeably.)
jitter_info = Property(HTML, depends_on=['jitter_perf'])
perf_info = Property(HTML, depends_on=['total_perf'])
status_str = Property(String, depends_on=['status'])
# - Handled by pybert_cntrl.py, upon user button clicks. (May contain "large overhead" variables.)
# - These are dependencies. So, they must be Array()s.
# - These are not.
# Note: Everything has been moved to pybert_cntrl.py.
# I was beginning to suspect flaky initialization behavior,
# due to the way in which I was splitting up the initialization.
# Also, this guarantees no GUI freeze-up.
# Default initialization
def __init__(self):
"""Plot setup occurs here."""
super(PyBERT, self).__init__()
plotdata = self.plotdata
# Running the simulation will fill in the 'plotdata' structure.
my_run_simulation(self, initial_run=True)
# Now, create all the various plots we need for our GUI.
# - DFE tab
plot1 = Plot(plotdata)
plot1.plot(("t_ns", "dfe_out"), type="line", color="blue")
plot1.plot(("t_ns", "clocks"), type="line", color="green")
plot1.plot(("t_ns", "lockeds"), type="line", color="red")
plot1.title = "DFE Output, Recovered Clocks, & Locked"
plot1.index_axis.title = "Time (ns)"
plot1.tools.append(PanTool(plot1, constrain=True, constrain_key=None, constrain_direction='x'))
zoom1 = ZoomTool(plot1, tool_mode="range", axis='index', always_on=False)
plot1.overlays.append(zoom1)
plot2 = Plot(plotdata)
plot2.plot(("t_ns", "ui_ests"), type="line", color="blue")
plot2.title = "CDR Adaptation"
plot2.index_axis.title = "Time (ns)"
plot2.value_axis.title = "UI (ps)"
plot2.index_range = plot1.index_range # Zoom x-axes in tandem.
plot3 = Plot(plotdata)
plot3.plot(('f_MHz_dfe', 'jitter_rejection_ratio'), type="line", color="blue")
plot3.title = "CDR/DFE Jitter Rejection Ratio"
plot3.index_axis.title = "Frequency (MHz)"
plot3.value_axis.title = "Ratio (dB)"
zoom3 = ZoomTool(plot3, tool_mode="range", axis='index', always_on=False)
plot3.overlays.append(zoom3)
plot4 = Plot(plotdata)
plot4.plot(('auto_corr'), type="line", color="blue")
plot4.title = "Received to Transmitted Bits Correlation"
plot4.index_axis.title = "Offset (bits)"
plot4.value_axis.title = "Correlation"
plot4.value_range.high_setting = 1
plot4.value_range.low_setting = 0
zoom4 = ZoomTool(plot4, tool_mode="range", axis='index', always_on=False)
plot4.overlays.append(zoom4)
plot9 = Plot(plotdata, auto_colors=['red', 'orange', 'yellow', 'green', 'blue', 'purple'])
for i in range(gNtaps):
plot9.plot(("tap_weight_index", "tap%d_weights" % (i + 1)), type="line", color="auto", name="tap%d"%(i+1))
plot9.title = "DFE Adaptation"
plot9.tools.append(PanTool(plot9, constrain=True, constrain_key=None, constrain_direction='x'))
zoom9 = ZoomTool(plot9, tool_mode="range", axis='index', always_on=False)
plot9.overlays.append(zoom9)
plot9.legend.visible = True
plot9.legend.align = 'ul'
container_dfe = GridPlotContainer(shape=(2,2))
container_dfe.add(plot2)
container_dfe.add(plot9)
container_dfe.add(plot1)
container_dfe.add(plot3)
self.plots_dfe = container_dfe
# - Impulse Responses tab
plot_h_chnl = Plot(plotdata)
plot_h_chnl.plot(("t_ns_chnl", "chnl_h"), type="line", color="blue")
plot_h_chnl.title = "Channel"
plot_h_chnl.index_axis.title = "Time (ns)"
plot_h_chnl.y_axis.title = "Impulse Response (V/ns)"
plot_h_tx = Plot(plotdata)
plot_h_tx.plot(("t_ns_chnl", "tx_out_h"), type="line", color="red", name="Cumulative")
plot_h_tx.title = "Channel + Tx Preemphasis"
plot_h_tx.index_axis.title = "Time (ns)"
plot_h_tx.y_axis.title = "Impulse Response (V/ns)"
plot_h_ctle = Plot(plotdata)
plot_h_ctle.plot(("t_ns_chnl", "ctle_out_h"), type="line", color="red", name="Cumulative")
plot_h_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_h_ctle.index_axis.title = "Time (ns)"
plot_h_ctle.y_axis.title = "Impulse Response (V/ns)"
plot_h_dfe = Plot(plotdata)
plot_h_dfe.plot(("t_ns_chnl", "dfe_out_h"), type="line", color="red", name="Cumulative")
plot_h_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_h_dfe.index_axis.title = "Time (ns)"
plot_h_dfe.y_axis.title = "Impulse Response (V/ns)"
container_h = GridPlotContainer(shape=(2,2))
container_h.add(plot_h_chnl)
container_h.add(plot_h_tx)
container_h.add(plot_h_ctle)
container_h.add(plot_h_dfe)
self.plots_h = container_h
# - Step Responses tab
plot_s_chnl = Plot(plotdata)
plot_s_chnl.plot(("t_ns_chnl", "chnl_s"), type="line", color="blue")
plot_s_chnl.title = "Channel"
plot_s_chnl.index_axis.title = "Time (ns)"
plot_s_chnl.y_axis.title = "Step Response (V)"
plot_s_tx = Plot(plotdata)
plot_s_tx.plot(("t_ns_chnl", "tx_s"), type="line", color="blue", name="Incremental")
plot_s_tx.plot(("t_ns_chnl", "tx_out_s"), type="line", color="red", name="Cumulative")
plot_s_tx.title = "Channel + Tx Preemphasis"
plot_s_tx.index_axis.title = "Time (ns)"
plot_s_tx.y_axis.title = "Step Response (V)"
plot_s_tx.legend.visible = True
plot_s_tx.legend.align = 'lr'
plot_s_ctle = Plot(plotdata)
plot_s_ctle.plot(("t_ns_chnl", "ctle_s"), type="line", color="blue", name="Incremental")
plot_s_ctle.plot(("t_ns_chnl", "ctle_out_s"), type="line", color="red", name="Cumulative")
plot_s_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_s_ctle.index_axis.title = "Time (ns)"
plot_s_ctle.y_axis.title = "Step Response (V)"
plot_s_ctle.legend.visible = True
plot_s_ctle.legend.align = 'lr'
plot_s_dfe = Plot(plotdata)
plot_s_dfe.plot(("t_ns_chnl", "dfe_s"), type="line", color="blue", name="Incremental")
plot_s_dfe.plot(("t_ns_chnl", "dfe_out_s"), type="line", color="red", name="Cumulative")
plot_s_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_s_dfe.index_axis.title = "Time (ns)"
plot_s_dfe.y_axis.title = "Step Response (V)"
plot_s_dfe.legend.visible = True
plot_s_dfe.legend.align = 'lr'
container_s = GridPlotContainer(shape=(2,2))
container_s.add(plot_s_chnl)
container_s.add(plot_s_tx)
container_s.add(plot_s_ctle)
container_s.add(plot_s_dfe)
self.plots_s = container_s
# - Frequency Responses tab
plot_H_chnl = Plot(plotdata)
plot_H_chnl.plot(("f_GHz", "chnl_H"), type="line", color="blue", index_scale='log')
plot_H_chnl.title = "Channel"
plot_H_chnl.index_axis.title = "Frequency (GHz)"
plot_H_chnl.y_axis.title = "Frequency Response (dB)"
plot_H_chnl.index_range.low_setting = 0.1
plot_H_chnl.index_range.high_setting = 40.
plot_H_tx = Plot(plotdata)
plot_H_tx.plot(("f_GHz", "tx_H"), type="line", color="blue", name="Incremental", index_scale='log')
plot_H_tx.plot(("f_GHz", "tx_out_H"), type="line", color="red", name="Cumulative", index_scale='log')
plot_H_tx.title = "Channel + Tx Preemphasis"
plot_H_tx.index_axis.title = "Frequency (GHz)"
plot_H_tx.y_axis.title = "Frequency Response (dB)"
plot_H_tx.index_range.low_setting = 0.1
plot_H_tx.index_range.high_setting = 40.
plot_H_tx.legend.visible = True
plot_H_tx.legend.align = 'll'
plot_H_ctle = Plot(plotdata)
plot_H_ctle.plot(("f_GHz", "ctle_H"), type="line", color="blue", name="Incremental", index_scale='log')
plot_H_ctle.plot(("f_GHz", "ctle_out_H"), type="line", color="red", name="Cumulative", index_scale='log')
plot_H_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_H_ctle.index_axis.title = "Frequency (GHz)"
plot_H_ctle.y_axis.title = "Frequency Response (dB)"
plot_H_ctle.index_range.low_setting = 0.1
plot_H_ctle.index_range.high_setting = 40.
plot_H_ctle.value_range.low_setting = -40.
plot_H_ctle.legend.visible = True
plot_H_ctle.legend.align = 'll'
plot_H_chnl.value_range = plot_H_ctle.value_range
plot_H_tx.value_range = plot_H_ctle.value_range
plot_H_dfe = Plot(plotdata)
plot_H_dfe.plot(("f_GHz", "dfe_H"), type="line", color="blue", name="Incremental", index_scale='log')
plot_H_dfe.plot(("f_GHz", "dfe_out_H"), type="line", color="red", name="Cumulative", index_scale='log')
plot_H_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_H_dfe.index_axis.title = "Frequency (GHz)"
plot_H_dfe.y_axis.title = "Frequency Response (dB)"
plot_H_dfe.index_range.low_setting = 0.1
plot_H_dfe.index_range.high_setting = 40.
plot_H_dfe.value_range = plot_H_ctle.value_range
plot_H_dfe.legend.visible = True
plot_H_dfe.legend.align = 'll'
container_H = GridPlotContainer(shape=(2,2))
container_H.add(plot_H_chnl)
container_H.add(plot_H_tx)
container_H.add(plot_H_ctle)
container_H.add(plot_H_dfe)
self.plots_H = container_H
# - Outputs tab
plot_out_chnl = Plot(plotdata)
plot_out_chnl.plot(("t_ns", "ideal_signal"), type="line", color="lightgrey")
plot_out_chnl.plot(("t_ns", "chnl_out"), type="line", color="blue")
plot_out_chnl.title = "Channel"
plot_out_chnl.index_axis.title = "Time (ns)"
plot_out_chnl.y_axis.title = "Output (V)"
zoom_out_chnl = ZoomTool(plot_out_chnl, tool_mode="range", axis='index', always_on=False)
plot_out_chnl.overlays.append(zoom_out_chnl)
plot_out_tx = Plot(plotdata)
plot_out_tx.plot(("t_ns", "tx_out"), type="line", color="blue")
plot_out_tx.title = "Channel + Tx Preemphasis (Noise added here.)"
plot_out_tx.index_axis.title = "Time (ns)"
plot_out_tx.y_axis.title = "Output (V)"
plot_out_tx.index_range = plot_out_chnl.index_range # Zoom x-axes in tandem.
plot_out_ctle = Plot(plotdata)
plot_out_ctle.plot(("t_ns", "ctle_out"), type="line", color="blue")
plot_out_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_out_ctle.index_axis.title = "Time (ns)"
plot_out_ctle.y_axis.title = "Output (V)"
plot_out_ctle.index_range = plot_out_chnl.index_range # Zoom x-axes in tandem.
plot_out_dfe = Plot(plotdata)
plot_out_dfe.plot(("t_ns", "dfe_out"), type="line", color="blue")
plot_out_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_out_dfe.index_axis.title = "Time (ns)"
plot_out_dfe.y_axis.title = "Output (V)"
plot_out_dfe.index_range = plot_out_chnl.index_range # Zoom x-axes in tandem.
container_out = GridPlotContainer(shape=(2,2))
container_out.add(plot_out_chnl)
container_out.add(plot_out_tx)
container_out.add(plot_out_ctle)
container_out.add(plot_out_dfe)
self.plots_out = container_out
# - Eye Diagrams tab
seg_map = dict(
red = [
(0.00, 0.00, 0.00), # black
(0.00001, 0.00, 0.00), # blue
(0.15, 0.00, 0.00), # cyan
(0.30, 0.00, 0.00), # green
(0.45, 1.00, 1.00), # yellow
(0.60, 1.00, 1.00), # orange
(0.75, 1.00, 1.00), # red
(0.90, 1.00, 1.00), # pink
(1.00, 1.00, 1.00) # white
],
green = [
(0.00, 0.00, 0.00), # black
(0.00001, 0.00, 0.00), # blue
(0.15, 0.50, 0.50), # cyan
(0.30, 0.50, 0.50), # green
(0.45, 1.00, 1.00), # yellow
(0.60, 0.50, 0.50), # orange
(0.75, 0.00, 0.00), # red
(0.90, 0.50, 0.50), # pink
(1.00, 1.00, 1.00) # white
],
blue = [
(0.00, 0.00, 0.00), # black
(1e-18, 0.50, 0.50), # blue
(0.15, 0.50, 0.50), # cyan
(0.30, 0.00, 0.00), # green
(0.45, 0.00, 0.00), # yellow
(0.60, 0.00, 0.00), # orange
(0.75, 0.00, 0.00), # red
(0.90, 0.50, 0.50), # pink
(1.00, 1.00, 1.00) # white
]
)
clr_map = ColorMapper.from_segment_map(seg_map)
self.clr_map = clr_map
plot_eye_chnl = Plot(plotdata)
plot_eye_chnl.img_plot("eye_chnl", colormap=clr_map,)
plot_eye_chnl.y_direction = 'normal'
plot_eye_chnl.components[0].y_direction = 'normal'
plot_eye_chnl.title = "Channel"
plot_eye_chnl.x_axis.title = "Time (ps)"
plot_eye_chnl.x_axis.orientation = "bottom"
plot_eye_chnl.y_axis.title = "Signal Level (V)"
plot_eye_chnl.x_grid.visible = True
plot_eye_chnl.y_grid.visible = True
plot_eye_chnl.x_grid.line_color = 'gray'
plot_eye_chnl.y_grid.line_color = 'gray'
plot_eye_tx = Plot(plotdata)
plot_eye_tx.img_plot("eye_tx", colormap=clr_map,)
plot_eye_tx.y_direction = 'normal'
plot_eye_tx.components[0].y_direction = 'normal'
plot_eye_tx.title = "Channel + Tx Preemphasis (Noise added here.)"
plot_eye_tx.x_axis.title = "Time (ps)"
plot_eye_tx.x_axis.orientation = "bottom"
plot_eye_tx.y_axis.title = "Signal Level (V)"
plot_eye_tx.x_grid.visible = True
plot_eye_tx.y_grid.visible = True
plot_eye_tx.x_grid.line_color = 'gray'
plot_eye_tx.y_grid.line_color = 'gray'
plot_eye_ctle = Plot(plotdata)
plot_eye_ctle.img_plot("eye_ctle", colormap=clr_map,)
plot_eye_ctle.y_direction = 'normal'
plot_eye_ctle.components[0].y_direction = 'normal'
plot_eye_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_eye_ctle.x_axis.title = "Time (ps)"
plot_eye_ctle.x_axis.orientation = "bottom"
plot_eye_ctle.y_axis.title = "Signal Level (V)"
plot_eye_ctle.x_grid.visible = True
plot_eye_ctle.y_grid.visible = True
plot_eye_ctle.x_grid.line_color = 'gray'
plot_eye_ctle.y_grid.line_color = 'gray'
plot_eye_dfe = Plot(plotdata)
plot_eye_dfe.img_plot("eye_dfe", colormap=clr_map,)
plot_eye_dfe.y_direction = 'normal'
plot_eye_dfe.components[0].y_direction = 'normal'
plot_eye_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_eye_dfe.x_axis.title = "Time (ps)"
plot_eye_dfe.x_axis.orientation = "bottom"
plot_eye_dfe.y_axis.title = "Signal Level (V)"
plot_eye_dfe.x_grid.visible = True
plot_eye_dfe.y_grid.visible = True
plot_eye_dfe.x_grid.line_color = 'gray'
plot_eye_dfe.y_grid.line_color = 'gray'
container_eye = GridPlotContainer(shape=(2,2))
container_eye.add(plot_eye_chnl)
container_eye.add(plot_eye_tx)
container_eye.add(plot_eye_ctle)
container_eye.add(plot_eye_dfe)
self.plots_eye = container_eye
# - Jitter Distributions tab
plot_jitter_dist_chnl = Plot(plotdata)
plot_jitter_dist_chnl.plot(('jitter_bins', 'jitter_chnl'), type="line", color="blue", name="Measured")
plot_jitter_dist_chnl.plot(('jitter_bins', 'jitter_ext_chnl'), type="line", color="red", name="Extrapolated")
plot_jitter_dist_chnl.title = "Channel"
plot_jitter_dist_chnl.index_axis.title = "Time (ps)"
plot_jitter_dist_chnl.value_axis.title = "Count"
plot_jitter_dist_chnl.legend.visible = True
plot_jitter_dist_chnl.legend.align = 'ur'
plot_jitter_dist_tx = Plot(plotdata)
plot_jitter_dist_tx.plot(('jitter_bins', 'jitter_tx'), type="line", color="blue", name="Measured")
plot_jitter_dist_tx.plot(('jitter_bins', 'jitter_ext_tx'), type="line", color="red", name="Extrapolated")
plot_jitter_dist_tx.title = "Channel + Tx Preemphasis (Noise added here.)"
plot_jitter_dist_tx.index_axis.title = "Time (ps)"
plot_jitter_dist_tx.value_axis.title = "Count"
plot_jitter_dist_tx.legend.visible = True
plot_jitter_dist_tx.legend.align = 'ur'
plot_jitter_dist_ctle = Plot(plotdata)
plot_jitter_dist_ctle.plot(('jitter_bins', 'jitter_ctle'), type="line", color="blue", name="Measured")
plot_jitter_dist_ctle.plot(('jitter_bins', 'jitter_ext_ctle'), type="line", color="red", name="Extrapolated")
plot_jitter_dist_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_jitter_dist_ctle.index_axis.title = "Time (ps)"
plot_jitter_dist_ctle.value_axis.title = "Count"
plot_jitter_dist_ctle.legend.visible = True
plot_jitter_dist_ctle.legend.align = 'ur'
plot_jitter_dist_dfe = Plot(plotdata)
plot_jitter_dist_dfe.plot(('jitter_bins', 'jitter_dfe'), type="line", color="blue", name="Measured")
plot_jitter_dist_dfe.plot(('jitter_bins', 'jitter_ext_dfe'), type="line", color="red", name="Extrapolated")
plot_jitter_dist_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_jitter_dist_dfe.index_axis.title = "Time (ps)"
plot_jitter_dist_dfe.value_axis.title = "Count"
plot_jitter_dist_dfe.legend.visible = True
plot_jitter_dist_dfe.legend.align = 'ur'
container_jitter_dist = GridPlotContainer(shape=(2,2))
container_jitter_dist.add(plot_jitter_dist_chnl)
container_jitter_dist.add(plot_jitter_dist_tx)
container_jitter_dist.add(plot_jitter_dist_ctle)
container_jitter_dist.add(plot_jitter_dist_dfe)
self.plots_jitter_dist = container_jitter_dist
# - Jitter Spectrums tab
plot_jitter_spec_chnl = Plot(plotdata)
plot_jitter_spec_chnl.plot(('f_MHz', 'jitter_spectrum_chnl'), type="line", color="blue", name="Total")
plot_jitter_spec_chnl.plot(('f_MHz', 'jitter_ind_spectrum_chnl'), type="line", color="red", name="Data Independent")
plot_jitter_spec_chnl.plot(('f_MHz', 'thresh_chnl'), type="line", color="magenta", name="Pj Threshold")
plot_jitter_spec_chnl.title = "Channel"
plot_jitter_spec_chnl.index_axis.title = "Frequency (MHz)"
plot_jitter_spec_chnl.value_axis.title = "|FFT(TIE)| (dBui)"
plot_jitter_spec_chnl.tools.append(PanTool(plot_jitter_spec_chnl, constrain=True, constrain_key=None, constrain_direction='x'))
zoom_jitter_spec_chnl = ZoomTool(plot_jitter_spec_chnl, tool_mode="range", axis='index', always_on=False)
plot_jitter_spec_chnl.overlays.append(zoom_jitter_spec_chnl)
plot_jitter_spec_chnl.legend.visible = True
plot_jitter_spec_chnl.legend.align = 'lr'
plot_jitter_spec_tx = Plot(plotdata)
plot_jitter_spec_tx.plot(('f_MHz', 'jitter_spectrum_tx'), type="line", color="blue", name="Total")
plot_jitter_spec_tx.plot(('f_MHz', 'jitter_ind_spectrum_tx'), type="line", color="red", name="Data Independent")
plot_jitter_spec_tx.plot(('f_MHz', 'thresh_tx'), type="line", color="magenta", name="Pj Threshold")
plot_jitter_spec_tx.title = "Channel + Tx Preemphasis (Noise added here.)"
plot_jitter_spec_tx.index_axis.title = "Frequency (MHz)"
plot_jitter_spec_tx.value_axis.title = "|FFT(TIE)| (dBui)"
plot_jitter_spec_tx.value_range.low_setting = -40.
plot_jitter_spec_tx.tools.append(PanTool(plot_jitter_spec_tx, constrain=True, constrain_key=None, constrain_direction='x'))
zoom_jitter_spec_tx = ZoomTool(plot_jitter_spec_tx, tool_mode="range", axis='index', always_on=False)
plot_jitter_spec_tx.overlays.append(zoom_jitter_spec_tx)
plot_jitter_spec_tx.legend.visible = True
plot_jitter_spec_tx.legend.align = 'lr'
plot_jitter_spec_chnl.value_range = plot_jitter_spec_tx.value_range
plot_jitter_spec_ctle = Plot(plotdata)
plot_jitter_spec_ctle.plot(('f_MHz', 'jitter_spectrum_ctle'), type="line", color="blue", name="Total")
plot_jitter_spec_ctle.plot(('f_MHz', 'jitter_ind_spectrum_ctle'), type="line", color="red", name="Data Independent")
plot_jitter_spec_ctle.plot(('f_MHz', 'thresh_ctle'), type="line", color="magenta", name="Pj Threshold")
plot_jitter_spec_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_jitter_spec_ctle.index_axis.title = "Frequency (MHz)"
plot_jitter_spec_ctle.value_axis.title = "|FFT(TIE)| (dBui)"
plot_jitter_spec_ctle.tools.append(PanTool(plot_jitter_spec_ctle, constrain=True, constrain_key=None, constrain_direction='x'))
zoom_jitter_spec_ctle = ZoomTool(plot_jitter_spec_ctle, tool_mode="range", axis='index', always_on=False)
plot_jitter_spec_ctle.overlays.append(zoom_jitter_spec_ctle)
plot_jitter_spec_ctle.legend.visible = True
plot_jitter_spec_ctle.legend.align = 'lr'
plot_jitter_spec_ctle.value_range = plot_jitter_spec_tx.value_range
plot_jitter_spec_dfe = Plot(plotdata)
plot_jitter_spec_dfe.plot(('f_MHz_dfe', 'jitter_spectrum_dfe'), type="line", color="blue", name="Total")
plot_jitter_spec_dfe.plot(('f_MHz_dfe', 'jitter_ind_spectrum_dfe'), type="line", color="red", name="Data Independent")
plot_jitter_spec_dfe.plot(('f_MHz_dfe', 'thresh_dfe'), type="line", color="magenta", name="Pj Threshold")
plot_jitter_spec_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_jitter_spec_dfe.index_axis.title = "Frequency (MHz)"
plot_jitter_spec_dfe.value_axis.title = "|FFT(TIE)| (dBui)"
plot_jitter_spec_dfe.tools.append(PanTool(plot_jitter_spec_dfe, constrain=True, constrain_key=None, constrain_direction='x'))
zoom_jitter_spec_dfe = ZoomTool(plot_jitter_spec_dfe, tool_mode="range", axis='index', always_on=False)
plot_jitter_spec_dfe.overlays.append(zoom_jitter_spec_dfe)
plot_jitter_spec_dfe.legend.visible = True
plot_jitter_spec_dfe.legend.align = 'lr'
plot_jitter_spec_dfe.value_range = plot_jitter_spec_tx.value_range
container_jitter_spec = GridPlotContainer(shape=(2,2))
container_jitter_spec.add(plot_jitter_spec_chnl)
container_jitter_spec.add(plot_jitter_spec_tx)
container_jitter_spec.add(plot_jitter_spec_ctle)
container_jitter_spec.add(plot_jitter_spec_dfe)
self.plots_jitter_spec = container_jitter_spec
# - Bathtub Curves tab
plot_bathtub_chnl = Plot(plotdata)
plot_bathtub_chnl.plot(("jitter_bins", "bathtub_chnl"), type="line", color="blue")
plot_bathtub_chnl.value_range.high_setting = 0
plot_bathtub_chnl.value_range.low_setting = -18
plot_bathtub_chnl.value_axis.tick_interval = 3
plot_bathtub_chnl.title = "Channel"
plot_bathtub_chnl.index_axis.title = "Time (ps)"
plot_bathtub_chnl.value_axis.title = "Log10(P(Transition occurs inside.))"
plot_bathtub_tx = Plot(plotdata)
plot_bathtub_tx.plot(("jitter_bins", "bathtub_tx"), type="line", color="blue")
plot_bathtub_tx.value_range.high_setting = 0
plot_bathtub_tx.value_range.low_setting = -18
plot_bathtub_tx.value_axis.tick_interval = 3
plot_bathtub_tx.title = "Channel + Tx Preemphasis (Noise added here.)"
plot_bathtub_tx.index_axis.title = "Time (ps)"
plot_bathtub_tx.value_axis.title = "Log10(P(Transition occurs inside.))"
plot_bathtub_ctle = Plot(plotdata)
plot_bathtub_ctle.plot(("jitter_bins", "bathtub_ctle"), type="line", color="blue")
plot_bathtub_ctle.value_range.high_setting = 0
plot_bathtub_ctle.value_range.low_setting = -18
plot_bathtub_ctle.value_axis.tick_interval = 3
plot_bathtub_ctle.title = "Channel + Tx Preemphasis + CTLE"
plot_bathtub_ctle.index_axis.title = "Time (ps)"
plot_bathtub_ctle.value_axis.title = "Log10(P(Transition occurs inside.))"
plot_bathtub_dfe = Plot(plotdata)
plot_bathtub_dfe.plot(("jitter_bins", "bathtub_dfe"), type="line", color="blue")
plot_bathtub_dfe.value_range.high_setting = 0
plot_bathtub_dfe.value_range.low_setting = -18
plot_bathtub_dfe.value_axis.tick_interval = 3
plot_bathtub_dfe.title = "Channel + Tx Preemphasis + CTLE + DFE"
plot_bathtub_dfe.index_axis.title = "Time (ps)"
plot_bathtub_dfe.value_axis.title = "Log10(P(Transition occurs inside.))"
container_bathtub = GridPlotContainer(shape=(2,2))
container_bathtub.add(plot_bathtub_chnl)
container_bathtub.add(plot_bathtub_tx)
container_bathtub.add(plot_bathtub_ctle)
container_bathtub.add(plot_bathtub_dfe)
self.plots_bathtub = container_bathtub
# These various plot customizing functions are left, for future reference.
# plot19.index_range = plot5.index_range # Zoom x-axes in tandem.
update_eyes(self)
# Dependent variable definitions
@cached_property
def _get_jitter_info(self):
isi_chnl = self.isi_chnl * 1.e12
dcd_chnl = self.dcd_chnl * 1.e12
pj_chnl = self.pj_chnl * 1.e12
rj_chnl = self.rj_chnl * 1.e12
isi_tx = self.isi_tx * 1.e12
dcd_tx = self.dcd_tx * 1.e12
pj_tx = self.pj_tx * 1.e12
rj_tx = self.rj_tx * 1.e12
isi_ctle = self.isi_ctle * 1.e12
dcd_ctle = self.dcd_ctle * 1.e12
pj_ctle = self.pj_ctle * 1.e12
rj_ctle = self.rj_ctle * 1.e12
isi_dfe = self.isi_dfe * 1.e12
dcd_dfe = self.dcd_dfe * 1.e12
pj_dfe = self.pj_dfe * 1.e12
rj_dfe = self.rj_dfe * 1.e12
isi_rej_tx = 1.e20
dcd_rej_tx = 1.e20
pj_rej_tx = 1.e20
rj_rej_tx = 1.e20
isi_rej_ctle = 1.e20
dcd_rej_ctle = 1.e20
pj_rej_ctle = 1.e20
rj_rej_ctle = 1.e20
isi_rej_dfe = 1.e20
dcd_rej_dfe = 1.e20
pj_rej_dfe = 1.e20
rj_rej_dfe = 1.e20
isi_rej_total = 1.e20
dcd_rej_total = 1.e20
pj_rej_total = 1.e20
rj_rej_total = 1.e20
if(isi_tx):
isi_rej_tx = isi_chnl / isi_tx
if(dcd_tx):
dcd_rej_tx = dcd_chnl / dcd_tx
if(pj_tx):
pj_rej_tx = pj_chnl / pj_tx
if(rj_tx):
rj_rej_tx = rj_chnl / rj_tx
if(isi_ctle):
isi_rej_ctle = isi_tx / isi_ctle
if(dcd_ctle):
dcd_rej_ctle = dcd_tx / dcd_ctle
if(pj_ctle):
pj_rej_ctle = pj_tx / pj_ctle
if(rj_ctle):
rj_rej_ctle = rj_tx / rj_ctle
if(isi_dfe):
isi_rej_dfe = isi_ctle / isi_dfe
if(dcd_dfe):
dcd_rej_dfe = dcd_ctle / dcd_dfe
if(pj_dfe):
pj_rej_dfe = pj_ctle / pj_dfe
if(rj_dfe):
rj_rej_dfe = rj_ctle / rj_dfe
if(isi_dfe):
isi_rej_total = isi_chnl / isi_dfe
if(dcd_dfe):
dcd_rej_total = dcd_chnl / dcd_dfe
if(pj_dfe):
pj_rej_total = pj_tx / pj_dfe
if(rj_dfe):
rj_rej_total = rj_tx / rj_dfe
info_str = '<H1>Jitter Rejection by Equalization Component</H1>\n'
info_str += '<H2>Tx Preemphasis</H2>\n'
info_str += '<TABLE border="1">\n'
info_str += '<TR align="center">\n'
info_str += "<TH>Jitter Component</TH><TH>Input (ps)</TH><TH>Output (ps)</TH><TH>Rejection (dB)</TH>\n"
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">ISI</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(isi_chnl, isi_tx, 10. * log10(isi_rej_tx))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">DCD</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(dcd_chnl, dcd_tx, 10. * log10(dcd_rej_tx))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Pj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>n/a</TD>\n' % \
(pj_chnl, pj_tx)
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Rj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>n/a</TD>\n' % \
(rj_chnl, rj_tx)
info_str += "</TR>\n"
info_str += "</TABLE>\n"
info_str += '<H2>CTLE</H2>\n'
info_str += '<TABLE border="1">\n'
info_str += '<TR align="center">\n'
info_str += "<TH>Jitter Component</TH><TH>Input (ps)</TH><TH>Output (ps)</TH><TH>Rejection (dB)</TH>\n"
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">ISI</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(isi_tx, isi_ctle, 10. * log10(isi_rej_ctle))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">DCD</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(dcd_tx, dcd_ctle, 10. * log10(dcd_rej_ctle))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Pj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(pj_tx, pj_ctle, 10. * log10(pj_rej_ctle))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Rj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(rj_tx, rj_ctle, 10. * log10(rj_rej_ctle))
info_str += "</TR>\n"
info_str += "</TABLE>\n"
info_str += '<H2>DFE</H2>\n'
info_str += '<TABLE border="1">\n'
info_str += '<TR align="center">\n'
info_str += "<TH>Jitter Component</TH><TH>Input (ps)</TH><TH>Output (ps)</TH><TH>Rejection (dB)</TH>\n"
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">ISI</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(isi_ctle, isi_dfe, 10. * log10(isi_rej_dfe))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">DCD</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(dcd_ctle, dcd_dfe, 10. * log10(dcd_rej_dfe))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Pj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(pj_ctle, pj_dfe, 10. * log10(pj_rej_dfe))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Rj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(rj_ctle, rj_dfe, 10. * log10(rj_rej_dfe))
info_str += "</TR>\n"
info_str += "</TABLE>\n"
info_str += '<H2>TOTAL</H2>\n'
info_str += '<TABLE border="1">\n'
info_str += '<TR align="center">\n'
info_str += "<TH>Jitter Component</TH><TH>Input (ps)</TH><TH>Output (ps)</TH><TH>Rejection (dB)</TH>\n"
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">ISI</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(isi_chnl, isi_dfe, 10. * log10(isi_rej_total))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">DCD</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(dcd_chnl, dcd_dfe, 10. * log10(dcd_rej_total))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Pj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(pj_tx, pj_dfe, 10. * log10(pj_rej_total))
info_str += "</TR>\n"
info_str += '<TR align="right">\n'
info_str += '<TD align="center">Rj</TD><TD>%6.3f</TD><TD>%6.3f</TD><TD>%4.1f</TD>\n' % \
(rj_tx, rj_dfe, 10. * log10(rj_rej_total))
info_str += "</TR>\n"
info_str += "</TABLE>\n"
return info_str
@cached_property
def _get_perf_info(self):
info_str = '<H2>Performance by Component</H2>\n'
info_str += ' <TABLE border="1">\n'
info_str += ' <TR align="center">\n'
info_str += ' <TH>Component</TH><TH>Performance (Msmpls./min.)</TH>\n'
info_str += ' </TR>\n'
info_str += ' <TR align="right">\n'
info_str += ' <TD align="center">Channel</TD><TD>%6.3f</TD>\n' % (self.channel_perf * 60.e-6)
info_str += ' </TR>\n'
info_str += ' <TR align="right">\n'
info_str += ' <TD align="center">Tx Preemphasis</TD><TD>%6.3f</TD>\n' % (self.tx_perf * 60.e-6)
info_str += ' </TR>\n'
info_str += ' <TR align="right">\n'
info_str += ' <TD align="center">CTLE</TD><TD>%6.3f</TD>\n' % (self.ctle_perf * 60.e-6)
info_str += ' </TR>\n'
info_str += ' <TR align="right">\n'
info_str += ' <TD align="center">DFE</TD><TD>%6.3f</TD>\n' % (self.dfe_perf * 60.e-6)
info_str += ' </TR>\n'
info_str += ' <TR align="right">\n'
info_str += ' <TD align="center">Jitter Analysis</TD><TD>%6.3f</TD>\n' % (self.jitter_perf * 60.e-6)
info_str += ' </TR>\n'
info_str += ' <TR align="right">\n'
info_str += ' <TD align="center">Plotting</TD><TD>%6.3f</TD>\n' % (self.plotting_perf * 60.e-6)
info_str += ' </TR>\n'
info_str += ' <TR align="right">\n'
info_str += ' <TD align="center">TOTAL</TD><TD>%6.3f</TD>\n' % (self.total_perf * 60.e-6)
info_str += ' </TR>\n'
info_str += ' </TABLE>\n'
return info_str
@cached_property
def _get_status_str(self):
perf_str = "%-20s | Perf. (Msmpls/min.): %4.1f" % (self.status, self.total_perf * 60.e-6)
jit_str = " | Jitter (ps): ISI=%6.3f DCD=%6.3f Pj=%6.3f Rj=%6.3f" % \
(self.isi_dfe * 1.e12, self.dcd_dfe * 1.e12, self.pj_dfe * 1.e12, self.rj_dfe * 1.e12)
dly_str = " | Channel Delay (ns): %5.3f" % (self.chnl_dly * 1.e9)
err_str = " | Bit errors detected: %d" % self.bit_errs
return perf_str + dly_str + jit_str + err_str
@cached_property
def _get_instructions(self):
help_str = "<H2>PyBERT User's Guide</H2>\n"
help_str += " <H3>Note to developers</H3>\n"
help_str += " This is NOT for you. Instead, open 'pybert/doc/build/html/index.html' in a browser.\n"
help_str += " <H3>PyBERT User Help Options</H3>\n"
help_str += " <UL>\n"
help_str += " <LI>Hover over any user-settable value in the <em>Config.</em> tab, for help message.</LI>\n"
help_str += ' <LI>Visit the PyBERT FAQ at: https://github.com/capn-freako/PyBERT/wiki/pybert_faq.</LI>\n'
help_str += ' <LI>Send e-mail to David Banas at capn.freako@gmail.com.</LI>\n'
help_str += " </UL>\n"
return help_str
if __name__ == '__main__':
PyBERT().configure_traits(view=traits_view)