Пример #1
0
    async def _search_helper(
        self,
        w: int,
        seg_min: int,
        seg_max: Optional[int],
        data_min: Optional[Any],
        data_max: Optional[Any],
    ) -> Tuple[Any, int, int, int]:
        # first, binary search on segments without changing width
        bin_iter = BinaryIterator(seg_min,
                                  seg_max,
                                  search_step=self._search_step)

        bval_min = bval_max = None
        if data_max is not None:
            bval_max = self.get_bin_search_info(data_max)[0]
        if data_min is not None:
            bval_min = self.get_bin_search_info(data_min)[0]
            bin_iter.set_current(seg_min)
            bin_iter.up(val=bval_min)
        elif seg_max is not None and data_max is not None:
            bin_iter.set_current(seg_max)
            bin_iter.down(val=bval_max)

        bounds = [[seg_min, bval_min, data_min], [seg_max, bval_max, data_max]]
        while bin_iter.has_next():
            cur_seg = bin_iter.get_next()
            cur_data = await self.get_data(cur_seg, w)
            cur_bval, up = self.get_bin_search_info(cur_data)
            if up:
                bounds[0][0] = cur_seg
                bounds[0][1] = cur_bval
                bounds[0][2] = cur_data
                bin_iter.up(val=cur_bval)
            else:
                bounds[1][0] = cur_seg
                bounds[1][1] = cur_bval
                bounds[1][2] = cur_data
                bin_iter.down(val=cur_bval)

        if bounds[1][1] is None:
            idx = 0
            seg_min = seg_max = bounds[0][0]
        elif bounds[0][1] is None:
            idx = 1
            seg_min = seg_max = bounds[1][0]
        else:
            idx = int(abs(bounds[1][1]) < abs(bounds[0][1]))
            seg_min = bounds[0][0]
            seg_max = bounds[1][0]

        opt_bnd = bounds[idx]
        opt_seg = opt_bnd[0]
        opt_data = opt_bnd[2]

        a_min = seg_min * w
        a_max = seg_max * w
        return opt_data, opt_seg, a_min, a_max
Пример #2
0
def design_amp(amp_specs, nch_db, pch_db):
    sim_env = amp_specs['sim_env']
    vdd = amp_specs['vdd']
    vtail = amp_specs['vtail']
    vgs_res = amp_specs['vgs_res']
    gain_min = amp_specs['gain_min']
    bw_min = amp_specs['bw_min']
    cload = amp_specs['cload']

    fun_ibiasn = nch_db.get_function('ibias', env=sim_env)
    fun_gmn = nch_db.get_function('gm', env=sim_env)
    fun_gdsn = nch_db.get_function('gds', env=sim_env)
    fun_cdn = nch_db.get_function('cdb', env=sim_env) + nch_db.get_function(
        'cds', env=sim_env)
    fun_cgsn = nch_db.get_function('cgs', env=sim_env)

    fun_ibiasp = pch_db.get_function('ibias', env=sim_env)
    fun_gdsp = pch_db.get_function('gds', env=sim_env)
    fun_cdp = pch_db.get_function('cdd', env=sim_env)

    vgsn_idx = nch_db.get_fun_arg_index('vgs')
    vgsn_min, vgsn_max = fun_ibiasn.get_input_range(vgsn_idx)
    num_pts = int(math.ceil((vgsn_max - vgsn_min) / vgs_res))
    vgs_list = np.linspace(vgsn_min, vgsn_max, num_pts + 1).tolist()

    vgsp_idx = pch_db.get_fun_arg_index('vgs')
    vgsp_min, vgsp_max = fun_ibiasp.get_input_range(vgsp_idx)
    # sweep vgs, find best point
    performance = None
    for vgsn_cur in vgs_list:
        vout = vgsn_cur + vtail

        narg = nch_db.get_fun_arg(vgs=vgsn_cur, vds=vgsn_cur, vbs=vtail)
        ibiasn_unit = fun_ibiasn(narg)
        gmn_unit = fun_gmn(narg)
        gdsn_unit = fun_gdsn(narg)
        cdn_unit = fun_cdn(narg)
        cgsn_unit = fun_cgsn(narg)

        # find max gain
        def gain_fun1(vgsp_test):
            parg_test = pch_db.get_fun_arg(vgs=vgsp_test,
                                           vds=vout - vdd,
                                           vbs=0)
            ibiasp_unit_test = fun_ibiasp(parg_test)
            gdsp_unit_test = fun_gdsp(parg_test)
            return gmn_unit / ibiasn_unit / (gdsn_unit / ibiasn_unit +
                                             gdsp_unit_test / ibiasp_unit_test)

        result = minimize_cost_golden_float(gain_fun1,
                                            gain_min,
                                            vgsp_min,
                                            vgsp_max,
                                            tol=vgs_res / 10)
        opt_vgsp = result.x
        if opt_vgsp is None:
            print('vgsn = %.4g, max gain: %.4g' % (vgsn_cur, result.vmax))
            break

        # get number of input fingers needed to achieve gain_max with minimum number of load fingers
        seg_in_init = fun_ibiasp(
            pch_db.get_fun_arg(vgs=opt_vgsp, vds=vout - vdd,
                               vbs=0)) * 2 / ibiasn_unit
        seg_in_init = int(round(seg_in_init / 2)) * 2
        # sweep gm size
        seg_in_iter = BinaryIterator(2, None, step=2)
        seg_in_iter.set_current(seg_in_init)
        while seg_in_iter.has_next():
            seg_in = seg_in_iter.get_next()
            ibiasn = seg_in * ibiasn_unit
            gmn = seg_in * gmn_unit
            gdsn = seg_in * gdsn_unit

            # sweep load size
            seg_load_iter = BinaryIterator(2, None, step=2)
            while seg_load_iter.has_next():
                seg_load = seg_load_iter.get_next()
                vbp, sgn = find_load_bias(pch_db, vdd, vout, vgsp_min,
                                          vgsp_max, ibiasn, seg_load,
                                          fun_ibiasp)

                if vbp is None:
                    if sgn > 0:
                        seg_load_iter.up()
                    else:
                        seg_load_iter.down()
                else:
                    parg = pch_db.get_fun_arg(vgs=vbp - vdd,
                                              vds=vout - vdd,
                                              vbs=0)
                    gdsp = seg_load * fun_gdsp(parg)
                    if gmn / (gdsp + gdsn) >= gain_min:
                        seg_load_iter.save_info((vbp, parg))
                        seg_load_iter.down()
                    else:
                        seg_load_iter.up()

            seg_load = seg_load_iter.get_last_save()
            if seg_load is None:
                # no load solution -> cannot meet gain spec.
                break

            vbp, parg = seg_load_iter.get_last_save_info()
            gdsp = seg_load * fun_gdsp(parg)
            cdp = seg_load * fun_cdp(parg)

            cdn = seg_in * cdn_unit
            cgsn = seg_in * cgsn_unit

            ro_cur = 1 / (gdsp + gdsn)
            gain_cur = gmn * ro_cur
            cpar_cur = cdn + cdp + (1 + 1 / gain_cur) * cgsn

            # check intrinsic bandwidth good
            if 1 / (ro_cur * cpar_cur * 2 * np.pi) < bw_min:
                break

            cload_cur = cload + cpar_cur
            bw_cur = 1 / (ro_cur * cload_cur * 2 * np.pi)
            if bw_cur < bw_min:
                seg_in_iter.up()
            else:
                seg_in_iter.save_info(
                    (seg_load, vbp, ibiasn, gain_cur, bw_cur))
                seg_in_iter.down()

        if seg_in_iter.get_last_save() is None:
            continue

        seg_in = seg_in_iter.get_last_save()
        seg_load, vbp, ibiasn, gain_cur, bw_cur = seg_in_iter.get_last_save_info(
        )
        if performance is None or performance[0] > ibiasn:
            performance = (ibiasn, gain_cur, bw_cur, seg_in, seg_load,
                           vgsn_cur, vbp)

    if performance is None:
        return None

    ibias_opt, gain_opt, bw_opt, seg_in, seg_load, vgs_in, vload = performance
    vio = vtail + vgs_in
    seg_tail, vbias = find_tail_bias(fun_ibiasn, nch_db, vtail, vgsn_min,
                                     vgsn_max, seg_in, ibias_opt)

    return dict(
        ibias=2 * ibias_opt,
        gain=gain_opt,
        bw=bw_opt,
        seg_in=seg_in,
        seg_load=seg_load,
        seg_tail=seg_tail,
        vtail=vbias,
        vindc=vio,
        voutdc=vio,
        vload=vload,
        vgs_in=vgs_in,
    )