np.where int_ntdc = np.cumsum(ntdc)*df int_ndco = np.cumsum(_ndco)*df int_ncomb = np.cumsum(_ncomb)*df f_ber = FREQS[np.where(1+2*int_ncomb > (1-2*BER)*(1+2*int_ncomb[-1]))[-1][0]] print("*** ", f_ber) tdc_steps.append(1.0/(16e6*_max_tdel)) f_bers.append(f_ber) plt.subplot(2,1,1) plt.semilogx(FNS, f_bers, color="k") plt.title("Offset from carrier containing 99% of power\n vs Closed loop PLL bandwidth") plt.xlabel("Loop bandwidth [Hz]") plt.ylabel("99% Power Offset [Hz]") #plt.grid() razavify() plt.subplot(2,1,2) plt.semilogx(FNS, tdc_steps, color="k") plt.title("Minimum number of TDC steps required\n vs PLL closed loopbandwidth") plt.xlabel("Loop bandwidth [Hz]") plt.ylabel("Minimum TDC steps") #plt.grid() razavify() adjust_subplot_space(2,1) plt.show() # plt.subplot(1,3,1) # plt.semilogx(FREQS, 20*np.log10(np.abs(g)), label="TDC noise") # plt.semilogx(FREQS, 20*np.log10(np.abs(1-g)**2), label="DCO noise") # plt.xlabel("Frequency [Hz]") # plt.ylabel("Magnitude response [dB]")
fs = 16e6 dt = 1 / fs steps = 100000 pn = 10**(-80 / 10) df = 1e6 krw = rw_gain(pn, df, steps, dt, m=1) print("krw=", krw) dco = DCO(kdco, f0, dt, krw=krw) osc_sig = np.zeros(steps) for n in range(steps): osc_sig[n] = dco.update(fctrl=0) osc_sig = make_signal(td=osc_sig, fs=fs) plot_pn_ssb2(osc_sig, line_fit=True) #plot_pn_ar_model(osc_sig) plt.legend() plt.title( "DCO SSB Phase Noise [dBc/Hz],\n $\mathtt{krw}$ fitted to $L(\Delta f=10^6)$ = -80 dBc/Hz" ) razavify(loc="lower left", bbox_to_anchor=[0, 0]) plt.xticks([1e2, 1e3, 1e4, 1e5, 1e6, 1e7], ["$10^2$", "$10^3$", "$10^4$", "$10^5$", "$10^6$", "$10^7$"]) plt.tight_layout() plt.savefig("dco_rw_pn.pdf")
main_pn_data = sim_data["sim_data"] if False: plt.legend() # plt.axhline(9990, color="r") # plt.axhline(10010, color="r") plot_td(main_pn_data["lf"], title="Loop Filter Output", tmax=50e-6) plt.title("PLL loop filter transient response") plt.ylabel("Loop filter output") plt.xlabel("Time [$\mu$s]") plt.ylim((9000, 10500)) # plt.ylim((8800, 10300)) # plt.plot((23e-6,23e-6),(8800, 9990), color="b", linestyle="--") # plt.text(25e-6, 10030, "Lock tolerance band", color="r", fontsize=12) # plt.text(23.5e-6, 9500, "Lock time", color="b", fontsize=12, rotation=90) razavify(loc="lower left", bbox_to_anchor=[0, 0]) plt.xticks([0, 0.5e-5, 1e-5, 1.5e-5, 2e-5], ["0", "5", "10", "15", "20"]) plt.yticks( [9000, 9200, 9400, 9600, 9800, 10000, 10200, 10400], ["9000", "9200", "9400", "9600", "9800", "10000", "12000", "14000"]) plt.xlim((0, 2e-5)) plt.tight_layout() plt.savefig("trans_loop_filter_fast.pdf") plt.show() # foo() foo() # # # plt.clf() # plt.cla() if False:
tmin=GEAR_SW_T * PN_CALC_SAFETY_FACTOR, label="$\\phi_n$") # rpm = noise_power_ar_model(pn_sig, fmax=FCLK/2, p=200, tmin=GEAR_SW_T) # print("int rpm = ", np.sqrt(rpm)) plot_pn_ar_model(em_pn, p=200, fmin=FBIN, tmin=GEAR_SW_T * PN_CALC_SAFETY_FACTOR, label="$\\phi_{em}$") plot_pi_pll_bbpd_pn(fmin=FBIN, **lfs["bbpd"]) plot_pi_pll_osc_pn(DCO_PN, DCO_PN_DF, fmin=FBIN, **lfs["bbpd"]) # plot_osc_pn_ideal(DCO_PN, DCO_PN_DF) plt.legend() # ticks = plt.xticks()[0] razavify(loc="lower center", bbox_to_anchor=[0.5, 0], legend_cols=1) ticks = np.geomspace(1e3, 1e6, 4) _ticks = ["$10^{%d}$" % np.log10(x) for x in ticks] plt.xticks(ticks, _ticks) ticks = np.linspace(-140, -80, 7) _ticks = ["%.0f" % x for x in ticks] plt.yticks(ticks, _ticks) plt.xlim((FBIN, FS / 2)) plt.figure(3) xticks = np.linspace(0, 20e-6, 5) plt.subplot(3, 1, 1) plot_td(sim_data["lf"], tmax=PLOT_SPAN, title="", dots=False) plt.ylabel("LF Out") plt.title("Loop Filter Output and Fine/Med DCO Codes") razavify(legend=False)
def opt_lf_num_bits(lf_params, min_bits, max_bits, rms_filt_error=0.1, noise_figure=1, sim_steps=1000, sim_runs=10, fpoints=512, mode="tdc", sigma_ph=0.1, tdc_in_stdev=1, plot=False): """ optimize number of bits for a digital direct form-I implementation using two's complement representation fixed point words with all parts of the data path with same data representation args: noise_figure: the maximum dB increase in noise due to loop filter quantization rms_filt_error : RMS value in dB for allowable filter error """ print("\n********************************************************") print("Optimizing loop filter digital direct form-I implementation for") print("number of bits in fixed point data words utilized") sign_bits = 1 # fint number of integer bits needed int_bits = n_int_bits(lf_params) print("\n* Integer bits = %d" % int_bits) """ Optimization for quantization noise """ print("\n* Optimizing for quantization noise:") # find optimal number of bits for quantization noise # generate white noise signal w simulating "regular" activity if lf_params["mode"] == "tdc": # w = np.floor(np.random.normal(0, 0.1*lf_params["m"], sim_steps)) w = np.floor(np.random.normal(0, tdc_in_stdev, sim_steps)) else: # BBPD mode, test sequence is random +/- 1 # w = np.random.normal(0, np.sqrt((1-2/np.pi)), sim_steps) w = np.random.choice((-1, 1), sim_steps) # ate pow_npd_post_lf = var_npd_post_lf( lf_params, mode=mode) # variance of TDC noise at loop filter lf_ideal = LoopFilterPIPhase(ignore_clk=True, **lf_params) x_ideal = np.zeros(sim_steps) for n in range(sim_steps): x_ideal[n] = lf_ideal.update(w[n], 0) mses = [] bit_range = range(min_bits - int_bits - 1, max_bits - int_bits) for frac_bits in bit_range: # use a large number of int bits to avoid overflow. Tuning here is with frac bits as runs = np.zeros(sim_runs) for m in range(sim_runs): lf_quant = LoopFilterPIPhase(ignore_clk=True, int_bits=32, frac_bits=frac_bits, quant_filt=False, **lf_params) x_quant = np.zeros(sim_steps) for n in range(sim_steps): x_quant[n] = lf_quant.update(w[n], 0) runs[m] = np.var(x_ideal - x_quant) mse = np.average(runs) print( "\tBits = %d,\t #(sign,int,frac) = (%d,%d,%d), \tQuant noise power = %E LSB^2" % ((sign_bits + int_bits + frac_bits), sign_bits, int_bits, frac_bits, mse)) mses.append(mse) threshold = (10**(noise_figure / 10.0) - 1) * pow_npd_post_lf print("Threshold=%E, PD noise post-LF=%E" % (threshold, pow_npd_post_lf)) for n, v in enumerate(mses): if v < threshold: break opt_frac_bits_qn = bit_range[n] print( "* Optimum int bits = %d, frac bits = %d, sign bits = 1, quant noise = %.3f LSB^2" % (int_bits, opt_frac_bits_qn, mses[n])) if plot: plt.figure(1) plt.clf() plt.semilogy(np.arange(min_bits, max_bits + 1), mses) plt.title("RMS Quantization Noise versus Filter Coefficient Bits") plt.xlabel("Total bits") plt.grid() plt.ylabel("DAC LSB$^2$") razavify() ticks = plt.yticks()[0] plt.yticks(ticks, ["10$^{%d}$" % int(round(np.log10(x))) for x in ticks]) plt.xlim(min_bits, max_bits) ticks = plt.xticks()[0] plt.xticks(ticks, ["%d" % x for x in ticks]) #//////////////////////////////////////////////////////////////////////////////////// """ Optimization for filter accuracy """ print("\n* Optimizing for filter design accuracy:") fmin = 1e2 fref = lf_params["fref"] b = [lf_params["b0"], lf_params["b1"]] a = [ 1, -1, ] f, h_ideal = scipy.signal.freqz(b, a, np.geomspace(fmin, fref / 2, fpoints), fs=fref) s = 2j * np.pi * f l = 2 * np.pi * lf_params["kpd"] * lf_params["kdco"] * h_ideal / s g = l / (1 + l) bit_range = range(min_bits - int_bits - 1, max_bits - int_bits) mses = [] # print(lf_params["b0"], lf_params["b1"]) for frac_bits in bit_range: _lf_params = quant_lf_params(lf_params, int_bits, frac_bits) b = [_lf_params["b0"], _lf_params["b1"]] # print(_lf_params["b0"], _lf_params["b1"]) f, h = scipy.signal.freqz(b, a, np.geomspace(fmin, fref / 2, fpoints), fs=fref) s = 2j * np.pi * f l = 2 * np.pi * lf_params["kpd"] * lf_params["kdco"] * h / s _g = l / (1 + l) # mses.append(np.var(20*np.log10(np.abs(h[1:]))-20*np.log10(np.abs(h_ideal[1:])))) mses.append( np.var(20 * np.log10(np.abs(g[1:])) - 20 * np.log10(np.abs(_g[1:])))) # print("\tN bits = %d\tMSE = %E dB^2"%(frac_bits+int_bits+sign_bits, mses[-1])) print( "\tBits = %d,\t #(sign,int,frac) = (%d,%d,%d), \tMSE = %E LSB^2" % ((sign_bits + int_bits + frac_bits), sign_bits, int_bits, frac_bits, mses[-1])) n = len(mses) - 1 for n, v in enumerate(mses): if v < rms_filt_error**2: break opt_frac_bits_filt_acc = bit_range[n] print( "* Optimum int bits = %d, frac bits = %d, sign_bits=1, quant noise = %E LSB^2" % (int_bits, opt_frac_bits_filt_acc, mses[n])) if plot: plt.figure(2) plt.clf() plt.semilogy(np.arange(min_bits, max_bits + 1), mses) plt.title("MSE Filter Error (dB) versus Filter Coefficient Bits") plt.xlabel("Total bits") plt.ylabel("MSE [dB$^2$]") plt.grid() razavify() ticks = plt.yticks()[0] plt.yticks(ticks, ["10$^{%d}$" % int(round(np.log10(x))) for x in ticks]) plt.xlim(min_bits, max_bits) ticks = plt.xticks()[0] plt.xticks(ticks, ["%d" % x for x in ticks]) frac_bits = max(opt_frac_bits_qn, opt_frac_bits_filt_acc) print("\n* Optimization complete:") print("\tInt bits = %d, frac bits = %d, sign bits = 1" % (int_bits, frac_bits)) print("\tTotal number bits = %d" % (int_bits + frac_bits + sign_bits)) return int_bits, frac_bits