Ejemplo n.º 1
0
def test_ctle2(fz, fp1, npts, order, gbw, dtmax, err_lim):
    # normalize frequencies
    fz = fz*dtmax
    fp1 = fp1*dtmax
    gbw = gbw*dtmax

    # read in data
    my_data = np.genfromtxt(DATA_FILE, delimiter=',', skip_header=1)
    t_resp = my_data[:, 1] - my_data[0, 1]
    v_resp = my_data[:, 2]

    # find timestep of oversampled data
    tover = np.median(np.diff(t_resp))
    assert np.all(np.isclose(np.diff(t_resp), tover))
    print(f'tover: {tover*1e12:0.3f} ps')

    # build interpolator for data
    my_interp = interp1d(t_resp, v_resp)
    svec = np.linspace(0, 1, npts)

    # find state-space representation of the CTLE
    A, B, C, D = calc_ctle_abcd(fz=fz, fp1=fp1, gbw=gbw)

    # calculate response using spline method
    # the list of timesteps used is one that was found to be particularly bad for the emulator
    W = calc_interp_w(npts=npts, order=order)
    ctle = SplineLDS(A=A, B=B, C=C, D=D, W=W)
    x = np.zeros((A.shape[0],), dtype=float)
    t = 0
    dtlist = [0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960,
              0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080,
              0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960,
              0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960,
              0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080]
    tlist = []
    ylist = []
    for dt in dtlist:
        tlist.append(t)
        x, y = ctle.calc_update(xo=x, inpt=my_interp(t+svec*dtmax), dt=dt)
        ylist.append(y)
        t += dt*dtmax

    # calculate measured values
    y_meas = interp_emu_res(tlist, ylist, dtmax, t_resp, order, npts)

    # find expected response of the CTLE
    num, den = calc_ctle_num_den(fz=fz, fp1=fp1, gbw=gbw)
    b, a, _ = cont2discrete((num, den), dt=tover/dtmax)
    y_expt = lfilter(b[0], a, v_resp)

    # uncomment to plot results
    # import matplotlib.pyplot as plt
    # plt.plot(t_resp, y_expt)
    # plt.plot(t_resp, y_meas)
    # plt.show()

    # calculate error
    rms_err = np.sqrt(np.mean((y_expt-y_meas)**2))
    print('rms_err:', rms_err)
    assert rms_err < err_lim
Ejemplo n.º 2
0
def run_simulation(t_resp, v_resp):
    # find timestep of oversampled data
    tover = np.median(np.diff(t_resp))
    assert np.all(np.isclose(np.diff(t_resp), tover))
    print(f'tover: {tover*1e12:0.3f} ps')

    # CLTE1
    nd1 = calc_ctle_num_den(fz=ctle1_fz * dtmax,
                            fp1=ctle1_fp * dtmax,
                            gbw=gbw * dtmax)
    b1, a1, _ = cont2discrete(nd1, dt=tover / dtmax)
    ctle1_out = lfilter(b1[0], a1, v_resp)

    # NL1
    nl_vsat = calc_tanh_vsat(nl_dB, 'dB', veval=nl_veval)
    nl_func = lambda v: tanhsat(v, nl_vsat)
    nl1_out = nl_func(ctle1_out)

    # CTLE2
    nd2 = calc_ctle_num_den(fz=ctle2_fz * dtmax,
                            fp1=ctle2_fp * dtmax,
                            gbw=gbw * dtmax)
    b2, a2, _ = cont2discrete(nd2, dt=tover / dtmax)
    ctle2_out = lfilter(b2[0], a2, nl1_out)

    # NL2
    nl2_out = nl_func(ctle2_out)

    # CTLE3
    nd3 = calc_ctle_num_den(fz=ctle3_fz * dtmax,
                            fp1=ctle3_fp * dtmax,
                            gbw=gbw * dtmax)
    b3, a3, _ = cont2discrete(nd3, dt=tover / dtmax)
    ctle3_out = lfilter(b3[0], a3, nl2_out)

    # NL3
    nl3_out = nl_func(ctle3_out)

    return {
        'ctle1_out': ctle1_out,
        'nl1_out': nl1_out,
        'ctle2_out': ctle2_out,
        'nl2_out': nl2_out,
        'ctle3_out': ctle3_out,
        'nl3_out': nl3_out
    }
Ejemplo n.º 3
0
def test_ctle_interp2(fz, fp1, npts, order, gbw, dtmax, err_lim, simulator,
                      real_type):
    # read in data
    my_data = np.genfromtxt(DATA_FILE, delimiter=',', skip_header=1)
    t_resp = my_data[:, 1] - my_data[0, 1]
    v_resp = my_data[:, 2]

    # find timestep of oversampled data
    tover = np.median(np.diff(t_resp))
    assert np.all(np.isclose(np.diff(t_resp), tover))
    print(f'tover: {tover*1e12:0.3f} ps')

    # build interpolator for input data
    my_interp = interp1d(t_resp, v_resp)
    svec = np.linspace(0, 1, npts)

    # generate model
    model = CTLEModel(fz=fz,
                      fp1=fp1,
                      gbw=gbw,
                      dtmax=dtmax,
                      module_name='model',
                      build_dir=BUILD_DIR,
                      clk='clk',
                      rst='rst',
                      real_type=real_type)
    model_file = model.compile_to_file(VerilogGenerator())

    # create IO dictionary
    ios = {}
    for k in range(npts):
        ios[f'in_{k}'] = fault.RealIn
        ios[f'out_{k}'] = fault.RealOut

    # declare circuit
    class dut(m.Circuit):
        name = 'test_ctle_interp2'
        io = m.IO(dt=fault.RealIn, clk=m.In(m.Clock), rst=m.BitIn, **ios)

    # create the tester
    tester = MsdslTester(dut, dut.clk)

    # convenience functions for reading/writing splines
    def poke_input_spline(vals):
        for k in range(npts):
            tester.poke(getattr(dut, f'in_{k}'), vals[k])

    def get_output_spline():
        retval = []
        for k in range(npts):
            retval.append(tester.get_value(getattr(dut, f'out_{k}')))
        return retval

    # initialize
    poke_input_spline([0] * npts)
    tester.poke(dut.dt, 0)
    tester.poke(dut.clk, 0)
    tester.poke(dut.rst, 1)
    tester.eval()

    # apply reset
    tester.step(2)

    # clear reset
    tester.poke(dut.rst, 0)
    tester.step(2)

    # apply segments (chosen from a particularly bad case observed on an FPGA)
    dtlist = [
        0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960,
        0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080,
        0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960,
        0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080, 0.960, 0.960,
        0.080, 0.960, 0.960, 0.080, 0.960, 0.960, 0.080
    ]
    t = 0
    tlist = []
    ylist = []
    for dt in dtlist:
        tlist.append(t)
        poke_input_spline(my_interp(t + svec * dtmax))
        tester.poke(dut.dt, dt)
        tester.eval()
        ylist.append(get_output_spline())
        tester.step(2)
        t += dt * dtmax

    # run the simulation
    tester.compile_and_run(
        directory=BUILD_DIR,
        simulator=simulator,
        ext_srcs=[model_file,
                  get_file('ctle_interp2/test_ctle_interp2.sv')],
        real_type=real_type,
        # dump_waveforms=True
    )

    # convert measurements to arrays
    ylist = [np.array([pt.value for pt in seg]) for seg in ylist]

    # calculate measured values
    idx0 = np.where(t_resp >= tlist[0])[0][0]
    idx1 = np.where(t_resp >= tlist[-1])[0][0]
    t_interp = t_resp[idx0:(idx1 + 1)]
    y_meas = interp_emu_res(tlist, ylist, dtmax, t_interp, order, npts)

    # find expected response of the CTLE
    num, den = calc_ctle_num_den(fz=fz * dtmax,
                                 fp1=fp1 * dtmax,
                                 gbw=gbw * dtmax)
    b, a, _ = cont2discrete((num, den), dt=tover / dtmax)
    y_expt = lfilter(b[0], a, v_resp)

    # uncomment to plot results
    # import matplotlib.pyplot as plt
    # plt.plot(t_resp, y_expt)
    # plt.plot(t_interp, y_meas)
    # plt.plot(tlist, [elem[0] for elem in ylist], 'o')
    # plt.legend(['y_expt', 'y_meas'])
    # plt.show()

    # calculate error
    rms_err = np.sqrt(np.mean((y_expt[idx0:(idx1 + 1)] - y_meas)**2))
    print('rms_err:', rms_err)
    assert rms_err < err_lim
Ejemplo n.º 4
0
def test_ctle_interp(simulator, real_type, fz=0.8e9, fp1=1.6e9, gbw=40e9,
                     dtmax=62.5e-12, nover=2500000, n_segs=25, err_lim=5e-4):
    # make sure test is repeatable
    # the seed is chosen to make sure the curve is interesting enough
    np.random.seed(4)

    # generate model
    model = CTLEModel(fz=fz, fp1=fp1, gbw=gbw, dtmax=dtmax, module_name='model',
                      build_dir=BUILD_DIR, clk='clk', rst='rst',
                      real_type=real_type)
    model_file = model.compile_to_file(VerilogGenerator())

    # create IO dictionary
    ios = {}
    for k in range(NPTS):
        ios[f'in_{k}'] = fault.RealIn
        ios[f'out_{k}'] = fault.RealOut

    # declare circuit
    class dut(m.Circuit):
        name = 'test_ctle_interp'
        io = m.IO(
            dt=fault.RealIn,
            clk=m.In(m.Clock),
            rst=m.BitIn,
            **ios
        )

    # create the tester
    tester = MsdslTester(dut, dut.clk)

    # convenience functions for reading/writing splines
    def poke_input_spline(vals):
        for k in range(NPTS):
            tester.poke(getattr(dut, f'in_{k}'), vals[k])
    def get_output_spline():
        retval = []
        for k in range(NPTS):
            retval.append(tester.get_value(getattr(dut, f'out_{k}')))
        return retval

    # define input segments
    min_v, max_v = -2, +2
    widths = np.random.uniform(0, 1, n_segs)
    times = np.concatenate(([0], np.cumsum(widths)))
    segs = [np.random.uniform(min_v, max_v, NPTS)]
    for k in range(1, n_segs):
        prv = make_cubic_func(*segs[k-1])(widths[k-1])
        nxt = np.random.uniform(min_v, max_v, NPTS-1)
        segs.append(np.concatenate(([prv], nxt)))

    # initialize
    poke_input_spline([0]*NPTS)
    tester.poke(dut.dt, 0)
    tester.poke(dut.clk, 0)
    tester.poke(dut.rst, 1)
    tester.eval()

    # apply reset
    tester.step(2)

    # clear reset
    tester.poke(dut.rst, 0)
    tester.step(2)

    # apply segments
    meas = []
    for k in range(n_segs):
        poke_input_spline(segs[k])
        tester.poke(dut.dt, widths[k])
        tester.eval()
        meas.append(get_output_spline())
        tester.step(2)

    # run the simulation
    parameters = {
        'in_range': 4.5,
        'out_range': 4.5
    }
    tester.compile_and_run(
        directory=BUILD_DIR,
        simulator=simulator,
        ext_srcs=[model_file, get_file('ctle_interp/test_ctle_interp.sv')],
        parameters=parameters,
        real_type=real_type,
        #dump_waveforms=True
    )

    # convert measurements to arrays
    meas = [np.array([pt.value for pt in seg]) for seg in meas]

    # create oversampled time vector and split into chunks corresponding to each cubic section
    tvec = np.linspace(0, times[-1], nover)
    ivec = [np.searchsorted(tvec, time) for time in times[1:-1]]
    tvec = np.split(tvec, ivec)

    # create a flat vector of input values
    xvec = [make_cubic_func(*segs[k])(tvec[k]-times[k]) for k in range(n_segs)]
    xvec = np.concatenate(xvec)

    # apply CTLE dynamics to flat input vector, then split output values into chunks
    # corresponding to each cubic section
    num, den = calc_ctle_num_den(fz=fz*dtmax, fp1=fp1*dtmax, gbw=gbw*dtmax)
    b, a, _ = cont2discrete((num, den), dt=times[-1]/(nover-1))
    yvec = lfilter(b[0], a, xvec)
    yvec = np.split(yvec, ivec)

    # evaluate the error for the chunk corresponding to each cubic section
    errs = []
    for k in range(n_segs):
        svec = np.arange(0, widths[k], 1/(NPTS-1))
        expt = interp1d(tvec[k]-times[k], yvec[k], bounds_error=False, fill_value='extrapolate')(svec)
        errs.append(meas[k][:len(expt)]-expt)

    # compute maximum error
    max_err = max([np.max(np.abs(elem)) for elem in errs])
    print(f'max_err: {max_err}')

    # make sure the worst-case error is within tolerance
    assert max_err < err_lim
Ejemplo n.º 5
0
def test_ctle(fz,
              fp1,
              npts,
              order=3,
              gbw=40e9,
              dtmax=62.5e-12,
              nover=100000,
              err_lim=1e-4):
    # normalize frequencies
    fz = fz * dtmax
    fp1 = fp1 * dtmax
    gbw = gbw * dtmax

    # calculate system representation
    num, den = calc_ctle_num_den(fz=fz, fp1=fp1, gbw=gbw)
    A, B, C, D = calc_ctle_abcd(fz=fz, fp1=fp1, gbw=gbw)

    # define input segments
    seg1 = make_cubic_func(-2, 0, -0.25, 1.75)
    seg2 = make_cubic_func(1.75, 0, 0.1, -0.3)
    seg3 = make_cubic_func(-0.3, -0.1, -0.1, 1.25)

    # calculate response using conventional method
    tvec = np.linspace(0, 1, nover)
    xvec = np.concatenate((seg1(tvec[:-1]), seg2(tvec[:-1]), seg3(tvec)))
    b, a, _ = cont2discrete((num, den), dt=1 / (nover - 1))
    y_expt = lfilter(b[0], a, xvec)

    # calculate response using spline method
    svec = np.linspace(0, 1, npts)
    W = calc_interp_w(npts=npts, order=order)
    ctle = SplineLDS(A=A, B=B, C=C, D=D, W=W)
    x = np.zeros((A.shape[0], ), dtype=float)
    x, y1_meas = ctle.calc_update(xo=x, inpt=seg1(svec), dt=1)
    x, y2_meas = ctle.calc_update(xo=x, inpt=seg2(svec), dt=1)
    x, y3_meas = ctle.calc_update(xo=x, inpt=seg3(svec), dt=1)

    # check the output a certain specific points
    y1_expt = y_expt[(0 * (nover - 1)):((1 * nover) - 0)]
    y2_expt = y_expt[(1 * (nover - 1)):((2 * nover) - 1)]
    y3_expt = y_expt[(2 * (nover - 1)):((3 * nover) - 2)]

    # sanity check to make sure slices are OK
    assert len(y1_expt) == nover
    assert len(y2_expt) == nover
    assert len(y3_expt) == nover

    # # uncomment for debugging
    # import matplotlib.pyplot as plt
    # plt.plot(tvec, y1_expt, '-')
    # plt.plot(svec, y1_meas, 'o')
    # plt.show()
    # plt.plot(tvec, y2_expt, '-')
    # plt.plot(svec, y2_meas, 'o')
    # plt.show()
    # plt.plot(tvec, y3_expt, '-')
    # plt.plot(svec, y3_meas, 'o')
    # plt.show()

    # sample output of conventional method
    y1_expt_i = interp1d(tvec, y1_expt)(svec)
    y2_expt_i = interp1d(tvec, y2_expt)(svec)
    y3_expt_i = interp1d(tvec, y3_expt)(svec)

    # run comparisons
    assert np.max(np.abs(y1_meas - y1_expt_i)) < err_lim
    assert np.max(np.abs(y2_meas - y2_expt_i)) < err_lim
    assert np.max(np.abs(y3_meas - y3_expt_i)) < err_lim
Ejemplo n.º 6
0
    def __init__(self, fz, fp1, fp2=None, gbw=None, **kwargs):
        # calculate the transfer function representation
        num, den = calc_ctle_num_den(fz=fz, fp1=fp1, fp2=fp2, gbw=gbw)

        # call the super constructor
        super().__init__(num=num, den=den, **kwargs)