def run_main(): interp_method = 'spline' sim_env = 'tt' nmos_spec = 'specs_mos_char/nch_w0d5.yaml' pmos_spec = 'specs_mos_char/pch_w0d5.yaml' intent = 'lvt' nch_db = get_db(nmos_spec, intent, interp_method=interp_method, sim_env=sim_env) nch_op = nch_db.query(vbs=0, vds=0.5, vgs=0.5) pprint.pprint(nch_op) # building circuit cir = LTICircuit() cir.add_transistor(nch_op, 'out', 'in', 'gnd', 'gnd', fg=2) cir.add_res(10e3, 'out', 'gnd') cir.add_cap(100e-15, 'out', 'gnd') # get gain/poles/zeros/bode plot trans_fun = cir.get_transfer_function('in', 'out', in_type='v') print('poles: %s' % trans_fun.poles) print('zeros: %s' % trans_fun.zeros) # note: don't use the gain attribute. For some reason it's broken print('gain: %.4g' % (trans_fun.num[-1] / trans_fun.den[-1])) fvec = np.logspace(5, 10, 1000) _, mag, phase = sig.bode(trans_fun, w=2 * np.pi * fvec) # get transient response state_space = cir.get_state_space('in', 'out', in_type='v') tvec = np.linspace(0, 1e-8, 1000) _, yvec = sig.step(state_space, T=tvec) _, (ax0, ax1) = plt.subplots(2, sharex=True) ax0.semilogx(fvec, mag) ax0.set_ylabel('Magnitude (dB)') ax1.semilogx(fvec, phase) ax1.set_ylabel('Phase (degrees)') ax1.set_xlabel('Frequency (Hz)') plt.figure(2) plt.plot(tvec, yvec) plt.ylabel('Output (V/V)') plt.xlabel('Time (s)') plt.show()
def characterize_casc_amp(env_list, lch, intent_list, fg_list, w_list, db_list, vbias_list, vload_list, vtail_list, vmid_list, vincm, voutcm, vdd, vin_clip, clip_ratio_min, cw, rw, fanout, ton, k_settle_targ, scale_min=0.25, scale_max=20): # compute DC transfer function curve and compute linearity spec ndb, pdb = db_list[0], db_list[-1] results = solve_casc_diff_dc(env_list, ndb, pdb, lch, intent_list, w_list, fg_list, vbias_list, vload_list, vtail_list, vmid_list, vdd, vincm, voutcm, vin_clip, clip_ratio_min, num_points=20) vmat_list, ratio_list, gain_list = results # compute settling ratio fg_tail, fg_in, fg_casc, fg_load = fg_list db_tail, db_in, db_casc, db_load = db_list w_tail, w_in, w_casc, w_load = w_list fzin = 1.0 / (2 * ton) wzin = 2 * np.pi * fzin tvec = np.linspace(0, ton, 200, endpoint=True) scale_list = [] cin_list = [] for env, vbias, vload, vtail, vmid, vmat in zip(env_list, vbias_list, vload_list, vtail_list, vmid_list, vmat_list): # step 1: get half circuit parameters and compute input capacitance in_par = db_in.query(env=env, w=w_in, vbs=-vtail, vds=vmid - vtail, vgs=vincm - vtail) casc_par = db_casc.query(env=env, w=w_casc, vbs=-vmid, vds=voutcm - vmid, vgs=vdd - vmid) load_par = db_load.query(env=env, w=w_load, vbs=0, vds=voutcm - vdd, vgs=vload - vdd) cir = LTICircuit() cir.add_transistor(in_par, 'mid', 'in', 'gnd', fg=fg_in) cir.add_transistor(casc_par, 'out', 'gnd', 'mid', fg=fg_casc) cir.add_transistor(load_par, 'out', 'gnd', 'gnd', fg=fg_load) zin = cir.get_impedance('in', fzin) cin = (1 / zin).imag / wzin cin_list.append(cin) # step 3A: construct differential circuit with vin_clip / 2. vts, vmps, vmns, vops, vons = vmat[-1, :] vinp = vincm + vin_clip / 4 vinn = vincm - vin_clip / 4 tail_par = db_tail.query(env=env, w=w_tail, vbs=0, vds=vts, vgs=vbias) inp_par = db_in.query(env=env, w=w_in, vbs=-vts, vds=vmns - vts, vgs=vinp - vts) inn_par = db_in.query(env=env, w=w_in, vbs=-vts, vds=vmps - vts, vgs=vinn - vts) cascp_par = db_casc.query(env=env, w=w_casc, vbs=-vmns, vds=vons - vmns, vgs=vdd - vmns) cascn_par = db_casc.query(env=env, w=w_casc, vbs=-vmps, vds=vops - vmps, vgs=vdd - vmps) loadp_par = db_load.query(env=env, w=w_load, vbs=0, vds=vons - vdd, vgs=vload - vdd) loadn_par = db_load.query(env=env, w=w_load, vbs=0, vds=vops - vdd, vgs=vload - vdd) cir = LTICircuit() cir.add_transistor(tail_par, 'tail', 'gnd', 'gnd', fg=fg_tail) cir.add_transistor(inp_par, 'midn', 'inp', 'tail', fg=fg_in) cir.add_transistor(inn_par, 'midp', 'inn', 'tail', fg=fg_in) cir.add_transistor(cascp_par, 'dn', 'gnd', 'midn', fg=fg_casc) cir.add_transistor(cascn_par, 'dp', 'gnd', 'midp', fg=fg_casc) cir.add_transistor(loadp_par, 'dn', 'gnd', 'gnd', fg=fg_load) cir.add_transistor(loadn_par, 'dp', 'gnd', 'gnd', fg=fg_load) cir.add_vcvs(0.5, 'inp', 'gnd', 'inac', 'gnd') cir.add_vcvs(-0.5, 'inn', 'gnd', 'inac', 'gnd') # step 3B: compute DC gain cir.add_vcvs(1, 'dac', 'gnd', 'dp', 'dn') dc_tf = cir.get_transfer_function('inac', 'dac') _, gain_vec = dc_tf.freqresp(w=[0.1]) dc_gain = gain_vec[0].real # step 3C add cap loading cload = fanout * cin cir.add_cap(cload, 'outp', 'gnd') cir.add_cap(cload, 'outn', 'gnd') cir.add_vcvs(1, 'outac', 'gnd', 'outp', 'outn') # step 4: find scale factor to achieve k_settle # noinspection PyUnresolvedReferences scale_swp = np.arange(scale_min, scale_max, 0.25).tolist() max_kset = 0 opt_scale = 0 for cur_scale in scale_swp: # add scaled wired parasitics cap_cur = cw / 2 / cur_scale res_cur = rw * cur_scale cir.add_cap(cap_cur, 'dp', 'gnd') cir.add_cap(cap_cur, 'dn', 'gnd') cir.add_cap(cap_cur, 'outp', 'gnd') cir.add_cap(cap_cur, 'outn', 'gnd') cir.add_res(res_cur, 'dp', 'outp') cir.add_res(res_cur, 'dn', 'outn') # get settling factor sys = cir.get_state_space('inac', 'outac') _, yvec = scipy.signal.step( sys, T=tvec) # type: Tuple[np.ndarray, np.ndarray] k_settle_cur = yvec[-1] / dc_gain # print('scale = %.4g, k_settle = %.4g' % (cur_scale, k_settle_cur)) # update next scale factor if k_settle_cur > max_kset: max_kset = k_settle_cur opt_scale = cur_scale else: # k_settle is decreasing, break break if max_kset >= k_settle_targ: break # remove wire parasitics cir.add_cap(-cap_cur, 'dp', 'gnd') cir.add_cap(-cap_cur, 'dn', 'gnd') cir.add_cap(-cap_cur, 'outp', 'gnd') cir.add_cap(-cap_cur, 'outn', 'gnd') cir.add_res(-res_cur, 'dp', 'outp') cir.add_res(-res_cur, 'dn', 'outn') if max_kset < k_settle_targ: raise ValueError('%s max kset = %.4g @ scale = %.4g, ' 'cannot meet settling time spec.' % (env, max_kset, opt_scale)) scale_list.append(opt_scale) return vmat_list, ratio_list, gain_list, scale_list, cin_list