def opt_kbbpd(lf_params, dco_pn, dco_pn_df, dco_power, temp, tdc_steps, div_n, kdco, fclk, opt_fmax, bbpd_tsu, bbpd_th, int_bits, frac_bits, sim_steps=10000, max_iter=15): cost = kbbpd_cost(lf_params, dco_pn, dco_pn_df, dco_power, temp, tdc_steps, div_n, kdco, fclk, opt_fmax, bbpd_tsu, bbpd_th, int_bits, frac_bits, sim_steps) return gss(cost, arg="kbbpd", params={}, _min=0, _max=0.3, max_iter=max_iter)
def phase_margin(tf_params, fmax): """ In degrees """ def cost(f): return abs(pll_otf(f, **tf_params)) fug = gss(cost, arg="f", params={}, _min=0, _max=fmax, target=1) return 180 + np.angle(pll_otf(fug, **tf_params), deg=True)
def bw_pi_pll(k, fz): def h2(f): return abs(pi_pll_tf2(f, k, fz))**2 return gss(h2, arg="f", params={}, _min=0, _max=2 * np.sqrt(k), target=0.5, conv_tol=1e-10)
def bw_solpf(fn, damping): def h2(f): return abs(solpf(f, fn, damping))**2 return gss(h2, arg="f", params={}, _min=0, _max=2 * fn, target=0.5, conv_tol=1e-10)
def opt_pll_tf_pi_controller_fast_settling(ph_margin, max_tsettle, tsettle_tol, fclk, oversamp=20): """ Optimized PI-controller PLL for phase noise and settling time. Subject to maximum settling time constrained by tsettle, tol. points=1025 for Romberg integration (2**k+1) """ def cost(damping): return opt_pll_tf_pi_ph_margin(damping, ph_margin, tsettle_tol, fclk) opt_damping = gss(cost, arg="damping", params={}, _min=0, _max=1.0, target=ph_margin, conv_tol=1e-5) def cost(tsettle): k = np.log(tsettle_tol)**2 / (opt_damping**2 * tsettle**2) fz = np.sqrt(k) / (2 * opt_damping * 2 * np.pi) return bw_pi_pll(k, fz) opt_tsettle = gss(cost, arg="tsettle", params={}, _min=0, _max=max_tsettle, target=fclk / oversamp) opt_bw = cost(opt_tsettle) print(opt_damping, opt_tsettle) #if opt_tsettle > max_tsettle: # raise Exception("Error: It is not possible to achieve the specified phase margin and lock time. \ # Specified tsettle=%E, actual=%E. Decrease phase margin and try again."%(max_tsettle, opt_tsettle)) print("For fast settling: opt pi tsettle = %E, damping = %f, bw = %E" % (opt_tsettle, opt_damping, opt_bw)) return pll_tf_pi_controller(opt_tsettle, tol, opt_damping)
def find_rw_k(f0, fs, samples, pn_db, df, seed=None, rw_seq=[]): """ Fits random phase walk gain parameter k to phase noise data args: f0 - oscillator fundamental frequency fs - 1/tstep of simulation samples = length of random walk sequence pn_db - target phase noise of oscillator df - offset of phase noise measurement seed - 32b value for predicable random walk sequence rw_seq - use if random walk sequence already calculated """ pn = 10**(pn_db / 20) def cost(k): osc = osc_td(f0, fs, samples, k, seed=seed, rw=rw_seq) print(pn, eval_model_pn(osc, f0, df)) return (pn - eval_model_pn(osc, f0, df))**2 k = gss(cost, arg="k", params={}, _min=0, _max=f0 / fs, conv_tol=1e-2) return k
def opt_pll_tf_pi_controller_tsettle(damping, tsettle, tol, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, points=1025, mode="tdc", sigma_ph=0.1, delay=0.0): """ Optimize tsettle of PI-controller PLL for phase noise with fixed damping """ def cost(tsettle): k = np.log(tol)**2 / (damping**2 * tsettle**2) fz = np.sqrt(k) / (2 * damping * 2 * np.pi) tf_params = dict(k=k, fz=fz, delay=delay) return pll_pn_power_est(pi_pll_tf, tf_params, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, points=points, mode=mode, sigma_ph=sigma_ph) return gss(cost, arg="tsettle", params={}, _min=0.01 * tsettle, _max=tsettle, target=0.0, conv_tol=1e-10)
rms = np.std(pn_sig.td) print("\nSimulated RMS PN:\t\t%.2E rad -> %.2f dB" % (rms, 20 * np.log10(rms))) est_rms_pn_design = np.sqrt(lfs["bbpd"]["int_pn"]) print("Filter design RMS PN Est:\t%.2E rad -> %.2f dB" % (est_rms_pn_design, 20 * np.log10(est_rms_pn_design))) SIM_PN_RMS[n] = rms EST_PN_RMS[n] = est_rms_pn_design def f(beta, target_pn): lf = design_filters(DCO_FOM, 0, FCLK, FOSC, MAX_ALPHA, DCO_POWER, KDCO1, beta) return abs(target_pn - lf["bbpd"]["int_pn"]) beta_opt = gss(f, "beta", {"target_pn": rms**2}, _min=0.5, _max=1.5) #1.0088433280171367 # alpha = alpha_opt(beta_opt) print("ITER %d RESULT:\tbeta = %.3E,\talpha = %.3E,\trms_pn = %.3E" % (n, beta_opt, alpha, rms)) BETAS[n] = beta_opt ALPHAS[n] = alpha print("\nITER\tBETA\tALPHA\trms") for n, v in enumerate(BETAS): print("%d\t%.8f\t%.8f\t%E" % (n, v, ALPHAS[n], SIM_PN_RMS[n])) # plt.plot(ALPHAS, SIM_PN_RMS**2) # plt.plot(ALPHAS, EST_PN_RMS**2) # plt.show() foo() plt.figure(1)
def opt_pll_tf_pi_controller_bbpd(tsettle, tol, pn_dco, pn_dco_df, n, kdco, fclk, fmax, delay=0, points=1025, max_iter=15): """ This does not work yet """ sigma_ph = 0.01 def cost(sigma_ph): m = 2 * np.pi tf_params = opt_pll_tf_pi_controller(tsettle, tol, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, points=points, mode="bbpd", sigma_ph=sigma_ph, delay=delay) _sigma_ph2 = pll_pn_power_est(pi_pll_tf, tf_params, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, mode="bbpd", sigma_ph=sigma_ph, points=1025) print(sigma_ph, np.sqrt(_sigma_ph2) / n, (sigma_ph - np.sqrt(_sigma_ph2) / n)**2) return (sigma_ph - np.sqrt(_sigma_ph2) / n)**2 sigma_ph = gss(cost, arg="sigma_ph", params={}, _min=0.0, _max=1 / n, max_iter=max_iter) print("opt sigma_ph", sigma_ph) m = 2 * np.pi tf = opt_pll_tf_pi_controller(tsettle, tol, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, points=points, mode="bbpd", sigma_ph=sigma_ph, delay=delay) return tf, sigma_ph
def opt_pll_tf_pi_controller(tsettle, tol, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, delay=0.0, points=1025, mode="tdc", sigma_ph=0.1): """ Optimized PI-controller PLL for phase noise and settling time. Subject to maximum settling time constrained by tsettle, tol. points=1025 for Romberg integration (2**k+1) """ tsettle_min = 0.01 * tsettle # have to constrain, 0 will cause div-by-0 def cost(tsettle): opt = opt_pll_tf_pi_controller_damping(tsettle, tol=tol, pn_dco=pn_dco, pn_dco_df=pn_dco_df, m=m, n=n, kdco=kdco, fclk=fclk, fmax=fmax, points=points, mode=mode, sigma_ph=sigma_ph, delay=delay) k = np.log(tol)**2 / (opt**2 * tsettle**2) fz = np.sqrt(k) / (2 * opt * 2 * np.pi) tf_params = dict(k=k, fz=fz, delay=delay) if fz > fmax: raise Exception( "Please increase fmax of loop filter optimization, frequency of TF zero in optimization exceeded fmax." ) return pll_pn_power_est(pi_pll_tf, tf_params, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, points=points, mode=mode, sigma_ph=sigma_ph) opt_tsettle = gss(cost, arg="tsettle", params={}, _min=tsettle_min, _max=tsettle, target=0.0, conv_tol=1e-5) opt_damping = opt_pll_tf_pi_controller_damping(opt_tsettle, tol, pn_dco, pn_dco_df, m, n, kdco, fclk, fmax, points=points, mode=mode, sigma_ph=sigma_ph, delay=delay) print("opt pi tsettle = %E, damping = %f" % (opt_tsettle, opt_damping)) return pll_tf_pi_controller(opt_tsettle, tol, opt_damping, delay=delay)
print("kbbpd = %f" % kbbpd) print("pow ideal = %f" % np.var(M2PI * ph)) print("pow bbpd = %f" % np.var(x * kbbpd)) print("diff pow = %f" % (np.var(x * kbbpd) - np.var(M2PI * ph))) print("Excess noise factor = %f" % (np.var(x * kbbpd) / np.var(M2PI * ph))) error = np.var(ph * M2PI - kbbpd * x) print("error = %f" % error) def cost(kbbpd): return np.mean((ph * M2PI - kbbpd * x)**2) opt_kbbpd = gss(cost, "kbbpd", {}, _min=kbbpd * 0.1, _max=kbbpd * 10) print("opt kbbpd = %f" % opt_kbbpd) print("\n*****************************************") print("MSE optimization") print("pow ideal = %f" % np.var(M2PI * ph)) print("pow bbpd = %f" % np.var(x * opt_kbbpd)) print("diff pow = %f" % (np.var(x * opt_kbbpd) - np.var(M2PI * ph))) print("Excess noise factor = %f" % (np.var(x * opt_kbbpd) / np.var(M2PI * ph))) error = np.var(ph * M2PI - opt_kbbpd * x) print("error = %f" % error) # plt.plot(x*kbbpd, label="BBPD") # plt.plot(M2PI*ph, label="ideal")
main_pn_data = pllsim_int_n(verbose=False, **main_sim_params) pn_sig = pn_signal(main_pn_data, DIV_N) # pow_pn = np.mean(pn_sig.td**2) # print(pow_pn) pow_pn = 20 * np.mean((pn_sig.td)**2) print(KBBPD, pow_pn) # error.append(np.abs(pow_pn - est_pow_pn)) return pow_pn return cost cost = kbbpd_cost(est_pow_pn) print(gss(cost, arg="KBBPD", params={}, _min=0, _max=0.3, max_iter=15)) for x, y in zip(np.geomspace(0.01, 0.1, 11), error): print(x, y) plt.subplot(2, 1, 1) plot_td(pn_sig) plt.subplot(2, 1, 2) plt.plot(main_pn_data["osc"].td - DIV_N * main_pn_data["clk"].td) plt.show() plot_pn_ssb2(pn_sig, dfmax=8e8, line_fit=False) plot_pn_ar_model(pn_sig, p=200, tmin=0) plot_lf_ideal_pn(DCO_PN, DCO_PN_DF, **lf_params) print(noise_power_ar_model(pn_sig, fmax=FCLK / 2, p=100)) plt.show()