def _make_circuit(cls, env_idx, gm_db, load_db, vtail, vg, vmid, vout, vbias, vb_gm, vb_load, cload, cpar1, w_dict, th_dict, stack_dict, seg_dict, gz, neg_cap=False, no_fb=False): cur_env = gm_db.env_list[env_idx] gm_db.set_dsn_params(w=w_dict['tail'], intent=th_dict['tail'], stack=stack_dict['tail']) tail1_params = gm_db.query(env=cur_env, vbs=0, vds=vtail - vb_gm, vgs=vbias - vb_gm) tail2_params = gm_db.query(env=cur_env, vbs=0, vds=vout - vb_gm, vgs=vbias - vb_gm) gm_db.set_dsn_params(w=w_dict['in'], intent=th_dict['in'], stack=stack_dict['in']) gm1_params = gm_db.query(env=cur_env, vbs=vb_gm - vtail, vds=vmid - vtail, vgs=vg - vtail) load_db.set_dsn_params(w=w_dict['load'], intent=th_dict['load'], stack=stack_dict['diode']) diode1_params = load_db.query(env=cur_env, vbs=0, vds=vmid - vb_load, vgs=vmid - vb_load) diode2_params = load_db.query(env=cur_env, vbs=0, vds=vout - vb_load, vgs=vmid - vb_load) load_db.set_dsn_params(stack=stack_dict['ngm']) ngm1_params = load_db.query(env=cur_env, vbs=0, vds=vmid - vb_load, vgs=vmid - vb_load) ngm2_params = load_db.query(env=cur_env, vbs=0, vds=vout - vb_load, vgs=vmid - vb_load) cir = LTICircuit() # stage 1 cir.add_transistor(tail1_params, 'tail', 'gnd', 'gnd', 'gnd', fg=seg_dict['tail1'], neg_cap=neg_cap) cir.add_transistor(gm1_params, 'midp', 'inn', 'tail', 'gnd', fg=seg_dict['in'], neg_cap=neg_cap) cir.add_transistor(gm1_params, 'midn', 'inp', 'tail', 'gnd', fg=seg_dict['in'], neg_cap=neg_cap) cir.add_transistor(diode1_params, 'midp', 'midp', 'gnd', 'gnd', fg=seg_dict['diode1'], neg_cap=neg_cap) cir.add_transistor(diode1_params, 'midn', 'midn', 'gnd', 'gnd', fg=seg_dict['diode1'], neg_cap=neg_cap) cir.add_transistor(ngm1_params, 'midn', 'midp', 'gnd', 'gnd', fg=seg_dict['ngm1'], neg_cap=neg_cap) cir.add_transistor(ngm1_params, 'midp', 'midn', 'gnd', 'gnd', fg=seg_dict['ngm1'], neg_cap=neg_cap) # stage 2 cir.add_transistor(tail2_params, 'outp', 'gnd', 'gnd', 'gnd', fg=seg_dict['tail2'], neg_cap=neg_cap) cir.add_transistor(tail2_params, 'outn', 'gnd', 'gnd', 'gnd', fg=seg_dict['tail2'], neg_cap=neg_cap) cir.add_transistor(diode2_params, 'outp', 'midn', 'gnd', 'gnd', fg=seg_dict['diode2'], neg_cap=neg_cap) cir.add_transistor(diode2_params, 'outn', 'midp', 'gnd', 'gnd', fg=seg_dict['diode2'], neg_cap=neg_cap) cir.add_transistor(ngm2_params, 'outp', 'midn', 'gnd', 'gnd', fg=seg_dict['ngm2'], neg_cap=neg_cap) cir.add_transistor(ngm2_params, 'outn', 'midp', 'gnd', 'gnd', fg=seg_dict['ngm2'], neg_cap=neg_cap) # parasitic cap cir.add_cap(cpar1, 'midp', 'gnd') cir.add_cap(cpar1, 'midn', 'gnd') # load cap cir.add_cap(cload, 'outp', 'gnd') cir.add_cap(cload, 'outn', 'gnd') # feedback resistors if not no_fb: cir.add_conductance(gz, 'xp', 'midn') cir.add_conductance(gz, 'xn', 'midp') # diff-to-single conversion cir.add_vcvs(0.5, 'inp', 'gnd', 'in', 'gnd') cir.add_vcvs(-0.5, 'inn', 'gnd', 'in', 'gnd') cir.add_vcvs(1, 'out', 'gnd', 'outp', 'outn') return cir
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