From f6a424a6b40635636dc9d9ed83faf89b5bfd1e25 Mon Sep 17 00:00:00 2001 From: David Banas Date: Mon, 9 Sep 2024 06:32:14 -0400 Subject: [PATCH] Rel. v6.2.1 --- PyAMI | 2 +- models/ibisami/example_rx.ami | 135 +++++++++++++++++++++++ models/ibisami/example_rx.ibs | 83 ++++++++++++++ pyproject.toml | 5 +- tests/conftest.py | 42 +++++++ tests/test_ibisami_rx_getwave.py | 63 +++++++++++ tests/test_ibisami_rx_getwave_clocked.py | 63 +++++++++++ tests/test_ibisami_rx_init.py | 63 +++++++++++ tox.ini | 2 + 9 files changed, 455 insertions(+), 3 deletions(-) create mode 100644 models/ibisami/example_rx.ami create mode 100644 models/ibisami/example_rx.ibs create mode 100644 tests/test_ibisami_rx_getwave.py create mode 100644 tests/test_ibisami_rx_getwave_clocked.py create mode 100644 tests/test_ibisami_rx_init.py diff --git a/PyAMI b/PyAMI index ed58876..185aa84 160000 --- a/PyAMI +++ b/PyAMI @@ -1 +1 @@ -Subproject commit ed588768f975e88f92ec7432b05f08b7ff34b252 +Subproject commit 185aa84d5a1edd4588bb87ed2bf9ca7bcec95ae1 diff --git a/models/ibisami/example_rx.ami b/models/ibisami/example_rx.ami new file mode 100644 index 0000000..26a1b03 --- /dev/null +++ b/models/ibisami/example_rx.ami @@ -0,0 +1,135 @@ +(example_rx + + (Description "Example Rx model from ibisami package.") + + (Reserved_Parameters + (AMI_Version + (Usage Info ) + (Type String ) + (Value "5.1" ) + (Description "Version of IBIS standard we comply with." ) + ) + (Init_Returns_Impulse + (Usage Info ) + (Type Boolean ) + (Value True ) + (Description "Model provides DFE adaptation approximation, in its AMI_Init() function." ) + ) + (GetWave_Exists + (Usage Info ) + (Type Boolean ) + (Value True ) + (Description "This model is dual-mode, with true DFE adaptation in GetWave()." ) + ) + ) + (Model_Specific + (ctle_mode + (Usage In ) + (Type Integer ) + (List 0 1 ) + (List_Tip "Off" "Manual" ) + (Description "CTLE operating mode." ) + ) + (ctle_freq + (Usage In ) + (Type Float ) + (Range 5000000000.0 1000000000.0 5000000000.0 ) + (Description "CTLE peaking frequency (Hz)." ) + ) + (ctle_mag + (Usage In ) + (Type Float ) + (Range 0.0 0.0 12.0 ) + (Description "CTLE peaking magnitude (dB)." ) + ) + (ctle_bandwidth + (Usage In ) + (Type Float ) + (Range 12000000000.0 5000000000.0 50000000000.0 ) + (Description "CTLE bandwidth (Hz)." ) + ) + (ctle_dcgain + (Usage In ) + (Type Float ) + (Range 0.0 -20.0 20.0 ) + (Description "CTLE d.c. gain (dB)." ) + ) + (dfe_mode + (Usage In ) + (Type Integer ) + (List 0 1 2 ) + (List_Tip "Off" "Manual" "Adaptive" ) + (Description "DFE operating mode." ) + ) + (dfe_ntaps + (Usage In ) + (Type Integer ) + (Value 5 ) + (Description "Number of DFE taps." ) + ) + (dfe_tap1 + (Usage In ) + (Type Float ) + (Value 0 ) + (Description "DFE tap 1 initial value." ) + ) + (dfe_tap2 + (Usage In ) + (Type Float ) + (Value 0 ) + (Description "DFE tap 2 initial value." ) + ) + (dfe_tap3 + (Usage In ) + (Type Float ) + (Value 0 ) + (Description "DFE tap 3 initial value." ) + ) + (dfe_tap4 + (Usage In ) + (Type Float ) + (Value 0 ) + (Description "DFE tap 4 initial value." ) + ) + (dfe_tap5 + (Usage In ) + (Type Float ) + (Value 0 ) + (Description "DFE tap 5 initial value." ) + ) + (dfe_vout + (Usage In ) + (Type Float ) + (Range 1.0 0.0 1.0 ) + (Description "DFE slicer output voltage magnitude." ) + ) + (dfe_gain + (Usage In ) + (Type Float ) + (Range 0.1 0.0 1.0 ) + (Description "DFE error feedback gain." ) + ) + (debug + (dbg_enable + (Usage In ) + (Type Boolean ) + (Value False ) + (Description "Master debug enable." ) + ) + (dump_dfe_adaptation + (Usage In ) + (Type Boolean ) + (Value False ) + (Description "Dump the DFE adaptation results." ) + ) + (dump_adaptation_input + (Usage In ) + (Type Boolean ) + (Value False ) + (Description "Dump the DFE summing node input." ) + ) + (Description "Debugging options.") + ) + ) + +) diff --git a/models/ibisami/example_rx.ibs b/models/ibisami/example_rx.ibs new file mode 100644 index 0000000..b30b88b --- /dev/null +++ b/models/ibisami/example_rx.ibs @@ -0,0 +1,83 @@ + + +[IBIS Ver] 7.1 +[File Name] example_rx.ibs +[File Rev] v0.1 + +[Date] 2024-07-05 + +[Source] ibisami public domain infrastructure + +[Disclaimer] +THIS MODEL IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS MODEL, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +[Notes] +This IBIS file was generated using the template file: "generic.ibs.em". + +[Copyright] Copyright (c) 2016 David Banas; all rights reserved World wide. +[Component] Example_Rx +[Manufacturer] (n/a) + +[Package] + +R_pkg 0.10 0.00 0.50 +L_pkg 10.00n 0.10n 50.00n +C_pkg 1.00p 0.01p 5.00p + + +[Pin] signal_name model_name R_pin L_pin C_pin +1p Tx_1_P example_rx +1n Tx_1_N example_rx +2p Tx_2_P example_rx +2n Tx_2_N example_rx +3p Tx_3_P example_rx +3n Tx_3_N example_rx + +[Diff_Pin] inv_pin vdiff tdelay_typ tdelay_min tdelay_max +1p 1n 0.1V NA NA NA +2p 2n 0.1V NA NA NA +3p 3n 0.1V NA NA NA + +[Model] example_rx +Model_type Input + +C_comp 1.00p 0.01p 5.00p +Vinl = 0.875 +Vinh = 0.925 + + +[Algorithmic Model] +Executable linux_gcc4.1.2_32 example_rx_x86.so example_rx.ami +Executable linux_gcc4.1.2_64 example_rx_x86_amd64.so example_rx.ami +Executable Windows_VisualStudio_32 example_rx_x86.dll example_rx.ami +Executable Windows_VisualStudio_64 example_rx_x86_amd64.dll example_rx.ami +[End Algorithmic Model] + +[Temperature_Range] 25.0 0.0 100.0 +[Voltage_Range] 1.80 1.62 1.98 + + +[GND Clamp] +-1.80 -1.000e+01 -1.000e+01 -1.000e+01 +0.00 0.000e+00 0.000e+00 0.000e+00 +1.80 1.800e-02 2.000e-02 1.636e-02 +3.60 3.600e-02 4.000e-02 3.273e-02 + +[Power Clamp] +-1.80 1.000e+01 1.000e+01 1.000e+01 +0.00 -0.000e+00 -0.000e+00 -0.000e+00 +1.80 -1.800e-02 -2.000e-02 -1.636e-02 +3.60 -3.600e-02 -4.000e-02 -3.273e-02 + + + +[END] diff --git a/pyproject.toml b/pyproject.toml index e17fd35..5b467f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "PipBERT" description = "Serial communication link bit error rate tester simulator, written in Python." -version = "6.2.0" +version = "6.2.1" # dynamic = ["version"] authors = [ {name = "David Banas", email = "capn.freako@gmail.com"} , {name = "David Patterson"} @@ -16,10 +16,11 @@ requires-python = ">=3.9,<3.13" license = {text = "BSD"} dependencies = [ "kiwisolver", - "PyIBIS-AMI>=6.1.1", + "PyIBIS-AMI>=6.2", "pyside6<6.7", "pyyaml>=6", "scikit-rf>=0.29", + "typing_extensions", ] keywords=["bert", "communication", "simulator"] classifiers=[ diff --git a/tests/conftest.py b/tests/conftest.py index e1b03b7..8f11e74 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,3 +17,45 @@ def dut_imp_len(): dut.impulse_length = 10 # (ns) dut.simulate(initial_run=True) yield dut + +@pytest.fixture(scope="module") +def ibisami_rx_init(): + """ + Return an initialized pybert object configured to use + an Rx IBIS-AMI model in statistical mode. + """ + dut = PyBERT(run_simulation=False, gui=False) + dut.rx_ibis_file = "models/ibisami/example_rx.ibs" + dut.rx_use_ibis = True + dut.rx_use_ami = True + dut.simulate(initial_run=True) + yield dut + +@pytest.fixture(scope="module") +def ibisami_rx_getwave(): + """ + Return an initialized pybert object configured to use + an Rx IBIS-AMI model in bit-by-bit mode. + """ + dut = PyBERT(run_simulation=False, gui=False) + dut.rx_ibis_file = "models/ibisami/example_rx.ibs" + dut.rx_use_ibis = True + dut.rx_use_ami = True + dut.rx_use_getwave = True + dut.simulate(initial_run=True) + yield dut + +@pytest.fixture(scope="module") +def ibisami_rx_getwave_clocked(): + """ + Return an initialized pybert object configured to use + an Rx IBIS-AMI model in bit-by-bit mode. + """ + dut = PyBERT(run_simulation=False, gui=False) + dut.rx_ibis_file = "models/ibisami/example_rx.ibs" + dut.rx_use_ibis = True + dut.rx_use_ami = True + dut.rx_use_getwave = True + dut.rx_use_clocks = True + dut.simulate(initial_run=True) + yield dut diff --git a/tests/test_ibisami_rx_getwave.py b/tests/test_ibisami_rx_getwave.py new file mode 100644 index 0000000..fce0318 --- /dev/null +++ b/tests/test_ibisami_rx_getwave.py @@ -0,0 +1,63 @@ +""" +Run some basic tests on a PyBERT instance w/ +an Rx IBIS-AMI model run in statistical mode. +""" + +import numpy as np +import pytest + + +@pytest.mark.usefixtures("ibisami_rx_getwave") +class TestIbisAmiRxGetWave(object): + """ + Basic tests of a properly initialized PyBERT w/ + user-defined channel impulse response length. + """ + + def test_status(self, dut): + """Test post-simulation status.""" + assert dut.status == "Ready.", "Status not 'Ready.'!" + + def test_perf(self, dut): + """Test simulation performance.""" + assert dut.total_perf > (1e6 / 60), "Performance dropped below 1 Msmpls/min.!" + + def test_ber(self, dut): + """Test simulation bit errors.""" + assert not dut.bit_errs, "Bit errors detected!" + + def test_dly(self, dut): + """Test channel delay.""" + assert dut.chnl_dly > 1e-9 and dut.chnl_dly < 10e-9, "Channel delay is out of range!" + + def test_isi(self, dut): + """Test ISI portion of jitter.""" + assert dut.isi_dfe < 50e-12, "ISI is too high!" + + def test_dcd(self, dut): + """Test DCD portion of jitter.""" + assert dut.dcd_dfe < 20e-12, "DCD is too high!" + + def test_pj(self, dut): + """Test periodic portion of jitter.""" + assert dut.pj_dfe < 20e-12, "Periodic jitter is too high!" + + def test_rj(self, dut): + """Test random portion of jitter.""" + assert dut.rj_dfe < 20e-12, "Random jitter is too high!" + + def test_lock(self, dut): + """Test CDR lock, by ensuring that last 20% of locked indication vector + is all True.""" + _lockeds = dut.lockeds + assert all(_lockeds[4 * len(_lockeds) // 5 :]), "CDR lock is unstable!" + + def test_adapt(self, dut): + """Test DFE lock, by ensuring that last 20% of all coefficient vectors + are stable to within +/-20% of their mean.""" + _weights = dut.adaptation # rows = step; cols = tap + _ws = np.array(list(zip(*_weights[4 * len(_weights) // 5 :]))) # zip(*x) = unzip(x) + _means = list(map(lambda xs: sum(xs) / len(xs), _ws)) + assert all( + list(map(lambda pr: pr[1] == 0 or all(abs(pr[0] - pr[1]) / pr[1] < 0.2), zip(_ws, _means))) + ), f"DFE adaptation is unstable! {max(_ws[-1])} {min(_ws[-1])}" diff --git a/tests/test_ibisami_rx_getwave_clocked.py b/tests/test_ibisami_rx_getwave_clocked.py new file mode 100644 index 0000000..bad3dee --- /dev/null +++ b/tests/test_ibisami_rx_getwave_clocked.py @@ -0,0 +1,63 @@ +""" +Run some basic tests on a PyBERT instance w/ +an Rx IBIS-AMI model run in statistical mode. +""" + +import numpy as np +import pytest + + +@pytest.mark.usefixtures("ibisami_rx_getwave_clocked") +class TestIbisAmiRxGetWaveClocked(object): + """ + Basic tests of a properly initialized PyBERT w/ + user-defined channel impulse response length. + """ + + def test_status(self, dut): + """Test post-simulation status.""" + assert dut.status == "Ready.", "Status not 'Ready.'!" + + def test_perf(self, dut): + """Test simulation performance.""" + assert dut.total_perf > (1e6 / 60), "Performance dropped below 1 Msmpls/min.!" + + def test_ber(self, dut): + """Test simulation bit errors.""" + assert not dut.bit_errs, "Bit errors detected!" + + def test_dly(self, dut): + """Test channel delay.""" + assert dut.chnl_dly > 1e-9 and dut.chnl_dly < 10e-9, "Channel delay is out of range!" + + def test_isi(self, dut): + """Test ISI portion of jitter.""" + assert dut.isi_dfe < 50e-12, "ISI is too high!" + + def test_dcd(self, dut): + """Test DCD portion of jitter.""" + assert dut.dcd_dfe < 20e-12, "DCD is too high!" + + def test_pj(self, dut): + """Test periodic portion of jitter.""" + assert dut.pj_dfe < 20e-12, "Periodic jitter is too high!" + + def test_rj(self, dut): + """Test random portion of jitter.""" + assert dut.rj_dfe < 20e-12, "Random jitter is too high!" + + def test_lock(self, dut): + """Test CDR lock, by ensuring that last 20% of locked indication vector + is all True.""" + _lockeds = dut.lockeds + assert all(_lockeds[4 * len(_lockeds) // 5 :]), "CDR lock is unstable!" + + def test_adapt(self, dut): + """Test DFE lock, by ensuring that last 20% of all coefficient vectors + are stable to within +/-20% of their mean.""" + _weights = dut.adaptation # rows = step; cols = tap + _ws = np.array(list(zip(*_weights[4 * len(_weights) // 5 :]))) # zip(*x) = unzip(x) + _means = list(map(lambda xs: sum(xs) / len(xs), _ws)) + assert all( + list(map(lambda pr: pr[1] == 0 or all(abs(pr[0] - pr[1]) / pr[1] < 0.2), zip(_ws, _means))) + ), f"DFE adaptation is unstable! {max(_ws[-1])} {min(_ws[-1])}" diff --git a/tests/test_ibisami_rx_init.py b/tests/test_ibisami_rx_init.py new file mode 100644 index 0000000..25ecd43 --- /dev/null +++ b/tests/test_ibisami_rx_init.py @@ -0,0 +1,63 @@ +""" +Run some basic tests on a PyBERT instance w/ +an Rx IBIS-AMI model run in statistical mode. +""" + +import numpy as np +import pytest + + +@pytest.mark.usefixtures("ibisami_rx_init") +class TestIbisAmiRxInit(object): + """ + Basic tests of a properly initialized PyBERT w/ + user-defined channel impulse response length. + """ + + def test_status(self, dut): + """Test post-simulation status.""" + assert dut.status == "Ready.", "Status not 'Ready.'!" + + def test_perf(self, dut): + """Test simulation performance.""" + assert dut.total_perf > (1e6 / 60), "Performance dropped below 1 Msmpls/min.!" + + def test_ber(self, dut): + """Test simulation bit errors.""" + assert not dut.bit_errs, "Bit errors detected!" + + def test_dly(self, dut): + """Test channel delay.""" + assert dut.chnl_dly > 1e-9 and dut.chnl_dly < 10e-9, "Channel delay is out of range!" + + def test_isi(self, dut): + """Test ISI portion of jitter.""" + assert dut.isi_dfe < 50e-12, "ISI is too high!" + + def test_dcd(self, dut): + """Test DCD portion of jitter.""" + assert dut.dcd_dfe < 20e-12, "DCD is too high!" + + def test_pj(self, dut): + """Test periodic portion of jitter.""" + assert dut.pj_dfe < 20e-12, "Periodic jitter is too high!" + + def test_rj(self, dut): + """Test random portion of jitter.""" + assert dut.rj_dfe < 20e-12, "Random jitter is too high!" + + def test_lock(self, dut): + """Test CDR lock, by ensuring that last 20% of locked indication vector + is all True.""" + _lockeds = dut.lockeds + assert all(_lockeds[4 * len(_lockeds) // 5 :]), "CDR lock is unstable!" + + def test_adapt(self, dut): + """Test DFE lock, by ensuring that last 20% of all coefficient vectors + are stable to within +/-20% of their mean.""" + _weights = dut.adaptation # rows = step; cols = tap + _ws = np.array(list(zip(*_weights[4 * len(_weights) // 5 :]))) # zip(*x) = unzip(x) + _means = list(map(lambda xs: sum(xs) / len(xs), _ws)) + assert all( + list(map(lambda pr: pr[1] == 0 or all(abs(pr[0] - pr[1]) / pr[1] < 0.2), zip(_ws, _means))) + ), f"DFE adaptation is unstable! {max(_ws[-1])} {min(_ws[-1])}" diff --git a/tox.ini b/tox.ini index f62f780..63b9d9d 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,8 @@ deps = pytest pytest-xdist pytest-cov + PyAMI/dist/PyIBIS_AMI-6.2.0-py3-none-any.whl + typing_extensions py39-mac: enable @ https://github.com/capn-freako/PyBERT/raw/master/deps/enable-6.1.0.dev0-cp39-cp39-macosx_12_0_arm64.whl py310-mac: enable @ https://github.com/capn-freako/PyBERT/raw/master/deps/enable-6.1.0.dev0-cp310-cp310-macosx_12_0_arm64.whl py311-mac: enable @ https://github.com/capn-freako/PyBERT/raw/master/deps/enable-6.2.0.dev0-cp311-cp311-macosx_10_9_universal2.whl