示例#1
0
文件: grid.py 项目: zhaokai-l/bag
    def interval_to_track(self, layer_id: int,
                          intv: Tuple[int, int]) -> Tuple[HalfInt, int]:
        """Convert given coordinates to track number and width.

        Parameters
        ----------
        layer_id : int
            the layer number.
        intv : Tuple[int, int]
            lower and upper coordinates perpendicular to the track direction.

        Returns
        -------
        track : HalfInt
            the track number
        width : int
            the track width, in number of tracks.
        """
        start, stop = intv
        htr = self.coord_to_htr(layer_id, (start + stop) // 2, RoundMode.NONE,
                                False)
        width = stop - start

        # binary search to take width override into account
        bin_iter = BinaryIterator(1, None)
        while bin_iter.has_next():
            cur_ntr = bin_iter.get_next()
            wire_width = self.get_wire_total_width(layer_id, cur_ntr)
            if wire_width == width:
                return HalfInt(htr), cur_ntr
            elif wire_width > width:
                bin_iter.down()
            else:
                bin_iter.up()

        # never found solution; width is not quantized.
        raise ValueError('Interval {} on layer {} width not quantized'.format(
            intv, layer_id))
示例#2
0
 def helper_fun(
     w_test: int, h_test: int, binfo: Optional[ArrayLayInfo], wc: int,
     hc: int, opt_a: int, iterator: BinaryIterator
 ) -> Tuple[Optional[ArrayLayInfo], int, int, int]:
     cur_area = w_test * h_test
     if cur_area >= opt_a:
         # this point can't beat current optimum
         iterator.down()
         return binfo, wc, hc, opt_a
     else:
         cur_info = self.get_blk_info(conn_layer, w_test, h_test, nx,
                                      ny, **kwargs)
         if cur_info is None:
             iterator.up()
             return binfo, wc, hc, opt_a
         else:
             # found new optimum
             iterator.down()
             return cur_info, w_test, h_test, cur_area
示例#3
0
    async def _design_lvl_shift_inv_pdn(self, pseg: int, nseg: int,
                                        out_inv_m: int, fanout: float,
                                        pinfo: Any, tbm_specs: Dict[str, Any],
                                        has_rst, dual_output, vin,
                                        vout) -> int:
        """
        This function figures out the NMOS nseg for the inverter given the target delay
        TODO: Make this use digitaldB instead
        """
        min_fanout: float = get_tech_global_info('bag3_digital')['min_fanout']
        inv_beta: float = get_tech_global_info('bag3_digital')['inv_beta']
        tb_params = self._get_full_tb_params()

        # Use a binary iterator to find the NMOS size
        max_nseg = int(np.round(nseg / min_fanout))
        iterator = BinaryIterator(1, max_nseg)
        load_seg = nseg + (pseg if has_rst else 0)
        inv_pseg = int(
            np.round(inv_beta * load_seg / ((1 + inv_beta) * fanout)))
        inv_pseg = 1 if inv_pseg == 0 else inv_pseg

        all_corners = get_tech_global_info(
            'bag3_digital')['signoff_envs']['all_corners']
        while iterator.has_next():
            inv_nseg = iterator.get_next()
            inv_in_nseg, inv_in_pseg = self._size_input_inv_for_fanout(
                inv_pseg, inv_nseg, pseg, nseg, fanout, has_rst)

            dut_params = self._get_lvl_shift_params_dict(
                pinfo, pseg, nseg, inv_pseg, inv_nseg, inv_in_pseg,
                inv_in_nseg, out_inv_m, has_rst, dual_output)
            dut = await self.async_new_dut('lvshift', STDCellWrapper,
                                           dut_params)
            err_worst = -1 * float('Inf')
            for env in all_corners['envs']:
                tbm_specs['sim_envs'] = [env]
                tbm_specs['sim_params']['vdd_in'] = all_corners[vin][env]
                tbm_specs['sim_params']['vdd'] = all_corners[vout][env]
                tbm = cast(CombLogicTimingTB,
                           self.make_tbm(CombLogicTimingTB, tbm_specs))

                sim_results = await self.async_simulate_tbm_obj(
                    f'sim_inv_nseg_{inv_nseg}_{env}', dut, tbm, tb_params)
                tdr_cur, tdf_cur = CombLogicTimingTB.get_output_delay(
                    sim_results.data,
                    tbm.specs,
                    'inb_buf',
                    'in_buf',
                    True,
                    in_pwr='vdd_in',
                    out_pwr='vdd_in')
                target_cur, _ = CombLogicTimingTB.get_output_delay(
                    sim_results.data,
                    tbm.specs,
                    'inb_buf',
                    'midp',
                    True,
                    in_pwr='vdd_in',
                    out_pwr='vdd')

                # Check for error conditions
                if math.isinf(np.max(tdr_cur)) or math.isinf(
                        np.max(tdf_cur)) or math.isinf(np.max(target_cur)):
                    raise ValueError(
                        "Got infinite delay in level shifter design script (sizing inverter NMOS)."
                    )
                if np.min(tdr_cur) < 0 or np.min(target_cur) < 0:
                    raise ValueError(
                        "Got negative delay in level shifter design script (sizing inverter NMOS). "
                    )

                err_cur = tdr_cur[0] - target_cur[0]
                if err_cur > err_worst:
                    err_worst = err_cur
                    worst_env = env
                    tdr = tdr_cur[0]
                    target = target_cur[0]
            '''
            print(f'iter: {inv_nseg}')
            print(f'env: {worst_env}, tdr: {tdr}, target: {target}')
            '''

            if tdr < target:
                iterator.down(target - tdr)
                iterator.save_info(inv_nseg)
            else:
                iterator.up(target - tdr)

        tmp_inv_nseg = iterator.get_last_save_info()
        if tmp_inv_nseg is None:
            tmp_inv_nseg = max_nseg
            self.warn(
                "Could not size pull down of inverter to meet required delay, picked the "
                "max inv_nseg based on min_fanout.")

        return tmp_inv_nseg
示例#4
0
def interp1d_no_nan(
        tvec: np.ndarray,
        yvec: np.ndarray) -> Callable[[Union[float, np.ndarray]], np.ndarray]:
    tsize = len(tvec)
    if np.isnan(tvec[-1]):
        bin_iter = BinaryIterator(1, tsize + 1)
        while bin_iter.has_next():
            delta = bin_iter.get_next()
            if np.isnan(tvec[tsize - delta]):
                bin_iter.save()
                bin_iter.up()
            else:
                bin_iter.down()
        tsize -= bin_iter.get_last_save()

    return interp1d(tvec[:tsize], yvec[:tsize], assume_sorted=True, copy=False)
示例#5
0
    async def async_design(self, pinfo: Mapping[str, Any], nbits: int,
                           rtol: float, atol: float, tbit: float, trf: float,
                           cload: float, mc_params: Param, num_cores: int,
                           target: Mapping[str, Any],
                           delay_cell_params: Mapping[str, Any],
                           **kwargs: Mapping[str, Any]) -> Mapping[str, Any]:
        td_min = target['td_min']
        td_max = target['td_max']
        t_max = target['t_max']
        td_sigma = target['td_sigma']
        tristate_seg = kwargs.get('tristate_seg', self.global_info['seg_min'])
        tristate_stack = kwargs.get('tristate_stack', 1)
        seg_buf_abs_max = kwargs.get('seg_buf_abs_max', 50)
        seg_buf_max_override = kwargs.get('seg_buf_max_override', None)
        seg_buf_min_override = kwargs.get('seg_buf_min_override', None)
        design_using_signoff = kwargs.get('design_using_signoff', False)
        mc_corner = kwargs.get('mc_corner', 'tt_25')
        mc_env_override = kwargs.get("mc_env_override", None)
        mc_worst_corner = kwargs.get("mc_worst_corner", True)
        plot_result: bool = kwargs.get('plot_result', False)
        dsn_monte_carlo: bool = kwargs.get('dsn_monte_carlo', True)
        gen_specs: Optional[Mapping[str,
                                    Any]] = kwargs.get('gen_cell_specs', None)
        gen_cell_args: Optional[Mapping[str, Any]] = kwargs.get(
            'gen_cell_args', None)

        # 0. Setup design environments and the testbench manager
        if design_using_signoff:
            dsn_envs = self.global_info['signoff_envs']
            dsn_env_names = dsn_envs['all_corners']['envs']
            dsn_env_vdds = dsn_envs['all_corners']['vdd']
        else:
            dsn_envs = self.global_info['dsn_envs']
            dsn_env_names = [
                env for dct in dsn_envs.values() for env in dct['env']
            ]
            dsn_env_vdds = {
                e: dsn_envs[c]['vdd']
                for c in dsn_envs.keys() for e in dsn_envs[c]['env']
            }

        if not mc_worst_corner and not mc_env_override:
            raise ValueError(
                "If not performing mc on the worst corner, specify mc_env_override!"
            )

        dut_pins = [
            'a_in', 'b_in', 'intout', 'VDD', 'VSS', f'sp<{nbits - 1}:0>',
            f'sn<{nbits - 1}:0>', 'a_in_buf'
        ]
        tbm_dict = {}
        for dsn_env in dsn_env_names:
            tbm_specs = self._get_tbm_specs(
                [dsn_env], dict(vdd={dsn_env: dsn_env_vdds[dsn_env]}),
                dut_pins, tbit, trf, cload, nbits, rtol, atol)
            tbm = cast(DigitalTranTB, self.make_tbm(DigitalTranTB, tbm_specs))
            tbm_dict[dsn_env] = tbm
        tbm_params = dict()

        # 1. Setup the base phase interpolator and extract the input cap
        pwr_domains = dict(b_in=('VSS', 'VDD'),
                           a_in=('VSS', 'VDD'),
                           out=('VSS', 'VDD'))
        cin_dut_conns = dict(a_in=1)
        for i in range(nbits):
            for name, value in zip(['a_en', 'b_en', 'a_enb', 'b_enb'],
                                   [1, 0, 0, 1]):
                cin_dut_conns[f'{name}<{i}>'] = value
                pwr_domains[f'{name}<{i}>'] = ('VSS', 'VDD')
        gen_params = dict(cls_name=PhaseInterpolator.get_qualified_name(),
                          params=dict(
                              pinfo=pinfo,
                              unit_params={
                                  'seg': tristate_seg,
                                  'stack_p': tristate_stack,
                                  'stack_n': tristate_stack
                              },
                              inv_params={
                                  'seg': 2,
                                  'stack_p': 1,
                                  'stack_n': 1
                              },
                              nbits=nbits,
                              draw_sub=True,
                              export_outb=True,
                              abut_tristates=True,
                          ))
        pi_in_cap = await self._get_input_cap(
            gen_params,
            'b_in',
            cload,
            cin_dut_conns,
            pwr_domains, [dict(pin='out', type='cap', value='c_out')],
            vdd=dsn_env_vdds['tt_25'],
            sim_envs=['tt_25'])
        # 2. Setup the base delay cell and extract it's input cap
        pin_names = ['bk1', 'ci_p', 'co_p', 'in_p', 'out_p']
        pwr_domains = {pin_name: ('VSS', 'VDD') for pin_name in pin_names}
        cin_dut_conns = dict(bk1=1, ci_p=0)
        load_list = [
            dict(pin='out_p', type='cap', value='c_out'),
            dict(pin='co_p', type='cap', value='c_out')
        ]
        gen_params = dict(cls_name=DelayCellCore.get_qualified_name(),
                          params=dict(pinfo=pinfo,
                                      stand_alone=True,
                                      **delay_cell_params))
        dc_in_cap = await self._get_input_cap(gen_params,
                                              'ci_p',
                                              cload,
                                              cin_dut_conns,
                                              pwr_domains,
                                              load_list,
                                              vdd=dsn_env_vdds['tt_25'],
                                              sim_envs=['tt_25'])

        # 3. Size the delay cell to be able to drive the phase interpolator
        dc_up_scale_factor = int(round(pi_in_cap / dc_in_cap))
        delay_cell_params['seg_dict']['in'] *= dc_up_scale_factor
        delay_cell_params['seg_dict']['out'] *= dc_up_scale_factor
        delay_cell_params['seg_dict']['sr'] *= dc_up_scale_factor
        nand_seg = delay_cell_params['seg_dict']['out'] * 2

        inv_in_cap = self.global_info['cin_inv']['cin_per_seg']
        inv_seg = int(round(np.sqrt(dc_in_cap * pi_in_cap) / inv_in_cap))

        # 4. Upsize the buffer inverter on the output
        self.log('-' * 80)
        if seg_buf_min_override:
            self.log(f"Minimum Segments Overridden to {seg_buf_min_override}")
            min_seg_buf = seg_buf_min_override
        else:
            self.log('Find the min size for all the codes to be positive')
            seg_buf_min_iter = BinaryIterator(2, None, 2)
            while seg_buf_min_iter.has_next():
                _seg_buf = seg_buf_min_iter.get_next()
                dut_params = self._update_dut_params(
                    pinfo,
                    nbits,
                    tristate_seg,
                    seg_buf=_seg_buf,
                    seg_inv=inv_seg,
                    seg_nand=nand_seg,
                    num_cores=num_cores,
                    dc_params=delay_cell_params)
                results = await self._measure_times(dsn_env_names,
                                                    tbm_dict,
                                                    dut_params,
                                                    tbm_params,
                                                    tbit,
                                                    nbits,
                                                    name=f'sim_min_{_seg_buf}')
                # find min and max delay step
                tstep_min = results['min_step']
                tstep_max = results['max_step']
                self.log(
                    f"Got min delay {tstep_min}, max delay {tstep_max}, with "
                    f"{_seg_buf} segments")
                if tstep_min < 0:
                    seg_buf_min_iter.up()
                else:
                    seg_buf_min_iter.save()
                    seg_buf_min_iter.down()
            min_seg_buf = seg_buf_min_iter.get_last_save()

        self.log('-' * 80)
        if seg_buf_max_override:
            self.log(f'Maximum Segments Overridden to {seg_buf_max_override}')
            max_seg_buf = seg_buf_max_override
        else:
            self.log(
                'Now find the maximum size for all the codes to be positive')
            seg_buf_max_iter = BinaryIterator(10, None, 2)
            max_reached = False
            while seg_buf_max_iter.has_next():
                _seg_buf = seg_buf_max_iter.get_next()
                dut_params = self._update_dut_params(
                    pinfo,
                    nbits,
                    tristate_seg,
                    seg_buf=_seg_buf,
                    seg_inv=inv_seg,
                    seg_nand=nand_seg,
                    num_cores=num_cores,
                    dc_params=delay_cell_params)
                results = await self._measure_times(dsn_env_names,
                                                    tbm_dict,
                                                    dut_params,
                                                    tbm_params,
                                                    tbit,
                                                    nbits,
                                                    name=f'sim_max_{_seg_buf}')
                # find min and max delay step
                tstep_min = results['min_step']
                tstep_max = results['max_step']
                self.log(
                    f"Got min delay {tstep_min}, max delay {tstep_max}, with "
                    f"{_seg_buf} segments")
                if tstep_min < 0:
                    seg_buf_max_iter.down()
                elif _seg_buf > seg_buf_abs_max:
                    max_reached = True
                    break
                else:
                    seg_buf_max_iter.save()
                    seg_buf_max_iter.up()
            max_seg_buf = seg_buf_max_iter.get_last_save(
            ) if not max_reached else seg_buf_abs_max
        self.log('-' * 80)
        self.log(
            f'Minimum Buffer segments to keep positive delays: {min_seg_buf}')
        self.log(
            f'Maximum Buffer segments to keep positive delays: {max_seg_buf}')

        seg_buf_bin_iter = BinaryIterator(min_seg_buf, max_seg_buf, 2)
        while seg_buf_bin_iter.has_next():
            _seg_buf = seg_buf_bin_iter.get_next()
            dut_params = self._update_dut_params(pinfo,
                                                 nbits,
                                                 tristate_seg,
                                                 seg_buf=_seg_buf,
                                                 seg_inv=inv_seg,
                                                 seg_nand=nand_seg,
                                                 num_cores=num_cores,
                                                 dc_params=delay_cell_params)
            results = await self._measure_times(dsn_env_names, tbm_dict,
                                                dut_params, tbm_params, tbit,
                                                nbits, 'sim_size')
            tdelay_max = results['max_dly']
            tstep_min = results['min_step']
            tstep_max = results['max_step']
            if tdelay_max > t_max and tstep_min > td_min and tstep_max < td_max:
                # delay constraint violated, linearity constraint met
                seg_buf_bin_iter.down()
            elif tdelay_max < t_max and (tstep_min < td_min
                                         or tstep_max > td_max):
                # delay constraint met, linearity constraint violated
                seg_buf_bin_iter.up()
            elif tdelay_max < t_max and tstep_min > td_min and tstep_max < td_max:
                # both constraints met
                seg_buf_bin_iter.save_info((dut_params, results))
                seg_buf_bin_iter.down()
            else:
                self.error(
                    'Both delay and linearity constraints violated, please relax specs.'
                )

        seg_buf_final = seg_buf_bin_iter.get_last_save()
        if not seg_buf_final:
            self.error(
                "Design failed!, unable to meet linearity specs within range of inv sizes"
            )
        self.log(
            f'Final output buffer size is {seg_buf_final}, before Monte Carlo sim.'
        )
        dut_params, results = seg_buf_bin_iter.get_last_save_info()

        if dsn_monte_carlo:
            # 5. Monte Carlo simulations
            mc_tbm_dict = {}
            if mc_worst_corner:
                mc_envs = [mc_corner]
                mc_vdd = dict(vdd={mc_corner: dsn_env_vdds[mc_corner]})
                mc_tbm_specs = self._get_tbm_specs([mc_corner], mc_vdd,
                                                   dut_pins, tbit, trf, cload,
                                                   nbits, rtol, atol)
                mc_tbm_specs['monte_carlo_params'] = mc_params
                mc_tbm = cast(DigitalTranTB,
                              self.make_tbm(DigitalTranTB, mc_tbm_specs))
                mc_tbm_dict[mc_corner] = mc_tbm
            else:
                # TODO
                mc_envs = ...
                ...

            dut_params = self._update_dut_params(pinfo,
                                                 nbits,
                                                 tristate_seg,
                                                 seg_buf=seg_buf_final,
                                                 seg_inv=inv_seg,
                                                 seg_nand=nand_seg,
                                                 num_cores=num_cores,
                                                 dc_params=delay_cell_params)

            mc_results = await self._measure_times(mc_envs,
                                                   mc_tbm_dict,
                                                   dut_params,
                                                   tbm_params,
                                                   tbit,
                                                   nbits,
                                                   name='sim_mc_pre')
            mc_factor, sigma_max = self._get_mc_factor(mc_results, td_sigma)
            self.log(f'Max std. dev. is {sigma_max}')
            self.log(f'Upscale everything by {mc_factor}')
            self.log('-' * 80)

            # 6. Final verification
            seg_unit_final = int(np.ceil(tristate_seg * mc_factor))
            seg_unit_final += seg_unit_final & 1  # layout constraint
            seg_buf_final = int(np.ceil(seg_buf_final * mc_factor))
            seg_buf_final += seg_buf_final & 1  # layout constraint
            delay_cell_params_scale = copy.deepcopy(delay_cell_params)
            for key in delay_cell_params['seg_dict']:
                delay_cell_params_scale['seg_dict'][key] = int(
                    np.ceil(delay_cell_params['seg_dict'][key] * mc_factor))
            nand_seg = int(np.ceil(nand_seg * mc_factor))
            inv_seg = int(np.ceil(inv_seg * mc_factor))
            dut_params = self._update_dut_params(
                pinfo,
                nbits,
                seg_unit_final,
                seg_buf=seg_buf_final,
                seg_inv=inv_seg,
                seg_nand=nand_seg,
                num_cores=num_cores,
                dc_params=delay_cell_params_scale)

            results = await self._measure_times(dsn_env_names,
                                                tbm_dict,
                                                dut_params,
                                                tbm_params,
                                                tbit,
                                                nbits,
                                                name='sim_sized')
            mc_results = await self._measure_times(mc_envs,
                                                   mc_tbm_dict,
                                                   dut_params,
                                                   tbm_params,
                                                   tbit,
                                                   nbits,
                                                   name='sim_mc_post')
            _, sigma_max = self._get_mc_factor(mc_results, td_sigma)
            self.log(f'Final Sigma: {sigma_max}')
            self.log('-' * 80)
        else:
            seg_unit_final = tristate_seg
            delay_cell_params_scale = delay_cell_params

        self.log('-' * 80)
        self.log(f'dsn_envs: {dsn_env_names}')
        self.log(f'final results:\n{pprint.pformat(results, width=100)}')

        if plot_result:
            from matplotlib import pyplot as plt
            plt.figure(1)
            ax: Any = plt.subplot(2, 1, 1)
            xvec = np.arange(0, results['tdr_step'].shape[1])
            for idx, sim_env in enumerate(dsn_env_names):
                tdr = results['tdrs'][idx, :-1].flatten()
                plt.step(xvec, tdr, where='mid', label=sim_env)
            ax.legend()
            ax.set_ylabel('Rise Delay (s)')
            ax = plt.subplot(2, 1, 2)
            for idx, sim_env in enumerate(dsn_env_names):
                tdr_step = results['tdr_step'][idx, :].flatten()
                ax.scatter(xvec, tdr_step, label=sim_env)
            ax.set_ylim(ymin=td_min, ymax=td_max)
            ax.legend()
            ax.set_ylabel('Rise Delay Step (s)')
            ax.set_xlabel('Code')
            plt.show()

        if gen_specs is not None and gen_cell_args is not None:
            gen_cell_specs = dict(
                lay_class=IPMarginTemplate.get_qualified_name(),
                params=dict(
                    cls_name=GenericWrapper.get_qualified_name(),
                    params=dict(
                        cls_name=PhaseInterpolatorWithDelay.get_qualified_name(
                        ),
                        params=dut_params,
                    ),
                ),
                **gen_specs,
            )
            return dict(gen_specs=gen_cell_specs, gen_args=gen_cell_args)

        return dict(
            seg_unit=seg_unit_final,
            seg_buf=seg_buf_final,
            seg_dc=delay_cell_params_scale['seg_dict'],
            nand_seg=nand_seg,
            inv_seg=inv_seg,
        )
示例#6
0
    def design(self, itarg_list, vds2_list, ft_min, stack_list=None):
        # type: (List[float], List[float], float, Optional[List[int]]) -> None
        """Design the diode load.

        Parameters
        ----------
        itarg_list : List[float]
            target single-ended bias current across simulation environments.
        vds2_list : List[float]
            list of op-amp stage 2 vds voltage across simulation environments.
        ft_min : float
            minimum transit frequency of the composit transistor.
        stack_list : Optional[List[int]]
            list of valid stack numbers.
        """
        if stack_list is None:
            stack_list = self._stack_list

        vgs_idx = self._db.get_fun_arg_index('vgs')

        num_stack = len(stack_list)

        self._best_op = None
        best_score = None
        for intent in self._intent_list:
            for w in self._valid_widths:
                for idx1 in range(num_stack):
                    stack1 = stack_list[idx1]
                    self._db.set_dsn_params(w=w, intent=intent, stack=stack1)
                    ib1 = self._db.get_function_list('ibias')
                    gm1 = self._db.get_function_list('gm')
                    gds1 = self._db.get_function_list('gds')
                    cd1 = self._db.get_function_list('cdd')
                    vgs1_min, vgs1_max = ib1[0].get_input_range(vgs_idx)

                    for idx2 in range(idx1, num_stack):
                        stack2 = stack_list[idx2]
                        self._db.set_dsn_params(stack=stack2)
                        ib2 = self._db.get_function_list('ibias')
                        gm2 = self._db.get_function_list('gm')
                        gds2 = self._db.get_function_list('gds')
                        cd2 = self._db.get_function_list('cdd')
                        vgs2_min, vgs2_max = ib2[0].get_input_range(vgs_idx)

                        vgs_min = max(vgs1_min, vgs2_min)
                        vgs_max = min(vgs1_max, vgs2_max)

                        seg1_iter = BinaryIterator(2, None, step=2)
                        while seg1_iter.has_next():
                            seg1 = seg1_iter.get_next()

                            all_neg = True
                            one_pass = False
                            seg2_iter = BinaryIterator(0, None, step=2)
                            while seg2_iter.has_next():
                                seg2 = seg2_iter.get_next()

                                vgs_list, err_code = self._solve_vgs(
                                    itarg_list, seg1, seg2, ib1, ib2, vgs_min,
                                    vgs_max)
                                if err_code < 0:
                                    # too few fingers
                                    seg2_iter.up()
                                elif err_code > 0:
                                    # too many fingers
                                    seg2_iter.down()
                                else:
                                    one_pass = True
                                    cur_score = self._compute_score(
                                        ft_min, seg1, seg2, gm1, gm2, gds1,
                                        gds2, cd1, cd2, vgs_list)

                                    if cur_score != -1:
                                        all_neg = False

                                    if cur_score < 0:
                                        seg2_iter.down()
                                    else:
                                        seg2_iter.save()
                                        seg2_iter.up()
                                        if best_score is None or cur_score > best_score:
                                            best_score = cur_score
                                            self._best_op = (intent, stack1,
                                                             stack2, w, seg1,
                                                             seg2, vgs_list,
                                                             vds2_list)

                            if seg2_iter.get_last_save() is None:
                                # no solution for seg2
                                if all_neg and one_pass:
                                    # all solutions encountered have negative resistance,
                                    # this means we have insufficent number of diode fingers.
                                    seg1_iter.up()
                                elif not one_pass:
                                    # exit immediately with no solutions at all; too many fingers
                                    seg1_iter.down()
                                else:
                                    # all positive resistance solution break V*_min specs.
                                    # this means we have too many number of fingers.
                                    seg1_iter.down()
                            else:
                                seg1_iter.save()
                                seg1_iter.up()
示例#7
0
    async def _get_stack(
            self,
            dut_params: Dict[str, Any],
            mm_specs: Dict[str, Any],
            r_targ: float,
            stack_max: Optional[int] = None) -> Tuple[np.ndarray, np.ndarray]:
        """Binary searches the stack size to hit target r_targ output resistance.
        If stack_max is None, we skip sizing. This is set when sizing the main driver.

        NOTE: this function modifies dut_params and tbm_specs.

        Parameters
        ----------
        dut_params: Dict[str, Any]
            Driver generator parameters
        mm_specs: Dict[str, Any]
            Specs for DriverPullUpDownMM
        r_targ:
            Target output resistance
        stack_max:
            Maximum allowed transistor stack size

        Returns
        -------
        r_pu, r_pd: Tuple[np.ndarray, np.ndarray]
            Measured pull-up / pull-down output resistance across given corners
        """

        if not stack_max:
            dut_params['stack'] = 1
            sim_id = f'stack_1'
            r_pu, r_pd = await self._get_resistance(sim_id, dut_params,
                                                    mm_specs)
            return r_pu, r_pd

        r_best = 0.0
        bin_iter = BinaryIterator(1, None)
        while bin_iter.has_next():
            cur_stack = bin_iter.get_next()
            while bin_iter.has_next() and cur_stack > stack_max:
                bin_iter.down(float('inf'))
                cur_stack = bin_iter.get_next()
            if cur_stack > stack_max:
                break
            dut_params['stack'] = cur_stack
            sim_id = f'stack_{cur_stack}'
            r_pu, r_pd = await self._get_resistance(sim_id, dut_params,
                                                    mm_specs)

            r_test = min(np.min(r_pu), np.min(r_pd))
            r_best = max(r_test, r_best)
            if r_targ > min(np.min(r_pu), np.min(r_pd)):
                bin_iter.up(r_targ - min(np.min(r_pu), np.min(r_pd)))
            else:
                bin_iter.save_info((cur_stack, r_pu, r_pd))
                bin_iter.down(r_targ - min(np.min(r_pu), np.min(r_pd)))

        save_info = bin_iter.get_last_save_info()
        if save_info is None:
            self.error(f'Cannot meet spec with stack_max = {stack_max}, '
                       f'r_best = {r_best:.4g}')
        stack, r_pu, r_pd = bin_iter.get_last_save_info()

        dut_params['stack'] = stack
        return r_pu, r_pd
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,
    )
示例#9
0
    def check_density_rule_edge(cls, n0, s0, s1, area):
        # type: (int, int, int, int) -> int
        """Compute edge block dimension from density spec.

        Given edge width or height (as dimension 0), find the missing dimension (dimension 1)
        such that density rule is met.

        Parameters
        ----------
        n0 : int
            edge length in dimension 0 as number of blocks.
        s0 : int
            dimension 0 block length in resolution units.
        s1 : int
            dimension 1 block length in resolution units.
        area : int
            the resistor area in the edge block that should be used for density spec.
            In resolution units squared.

        Returns
        -------
        n1 : int
            edge length in dimension 1 as number of blocks.
        """
        density = cls.get_res_density()
        # convert to float so we're doing floating point comparison
        area = float(area)

        bin_iter = BinaryIterator(1, None)
        a0 = n0 * s0 * s1
        while bin_iter.has_next():
            n1 = bin_iter.get_next()
            if area <= a0 * n1 * density:
                bin_iter.save()
                bin_iter.down()
            else:
                bin_iter.up()

        return bin_iter.get_last_save()
示例#10
0
    def find_edge_size(
            self,  # type: ResTech
            grid,  # type: RoutingGrid
            core_info,  # type: Dict[str, Any]
            is_lr_edge,  # type: bool
            params,  # type: Dict[str, Any]
            blk1,  # type: int
            max_blk_ext,  # type: int
    ):
        # type: (...) -> Tuple[int, Dict[str, Any]]
        """Compute resistor edge size that meets DRC rules.

        Calculate edge dimension (width for LR edge, height for TB edge) that meets DRC rules

        Parameters
        ----------
        grid : RoutingGrid
            the RoutingGrid object.
        core_info : Dict[str, Any]
            core layout information dictionary.
        is_lr_edge : bool
            True if this is left/right edge, False if this is top/bottom edge.
        params : Dict[str, Any]
            the resistor parameters dictionary.
        blk1 : int
            dimension1 block size in resolution units.
        max_blk_ext : int
            maximum number of blocks we can extend by.

        Returns
        -------
        n1 : int
            edge length in dimension 1 as number of blocks.
        layout_info : Dict[str, Any]
            the edge layout information dictionary.
        """

        bin_iter = BinaryIterator(1, max_blk_ext + 2)
        ans = None
        while bin_iter.has_next():
            n1 = bin_iter.get_next()

            if is_lr_edge:
                tmp = self.get_lr_edge_info(grid, core_info, n1 * blk1,
                                            **params)
            else:
                tmp = self.get_tb_edge_info(grid, core_info, n1 * blk1,
                                            **params)

            if tmp is None:
                bin_iter.up()
            else:
                ans = tmp
                bin_iter.save()
                bin_iter.down()

        if ans is None:
            raise ValueError('failed to find DRC clean core with maximum %d '
                             'additional block pitches.' % max_blk_ext)

        return bin_iter.get_last_save(), ans
示例#11
0
    def find_core_size(
            self,  # type: ResTech
            grid,  # type: RoutingGrid
            params,  # type: Dict[str, Any]
            wres,  # type: int
            hres,  # type: int
            wblk,  # type: int
            hblk,  # type: int
            ext_dir,  # type: str
            max_blk_ext,  # type: int
    ):
        # type: (...) -> Tuple[int, int, Dict[str, Any]]
        """Compute resistor core size that meets DRC rules.
        
        Given current resistor block size and the block pitch, increase the resistor block
        size if necessary to meet DRC rules.
        
        Parameters
        ----------
        grid : RoutingGrid
            the RoutingGrid object.
        params : Dict[str, Any]
            the resistor parameters dictionary.
        wres : int
            resistor core width, in resolution units.
        hres : int
            resistor core height, in resolution units.
        wblk : int
            the horizontal block pitch, in resolution units.
        hblk : int
            the vertical block pitch, in resolution units.
        ext_dir : Optional[str]
            if equal to 'x', then we will only stretch the resistor core horizontally.  If equal
            to 'y', we will only stretch the resistor core vertically.  Otherwise, we will find
            the resistor core with the minimum area that meets the density spec.
        max_blk_ext : int
            number of block pitches we can extend the resistor core size by.  If we cannot
            find a valid core size by extending this many block pitches, we declare failure.
        
        Returns
        -------
        nxblk : int
            width of the resistor core, in units of wblk.
        nyblk : int
            height of the resistor core, in units of hblk.
        layout_info : Dict[str, Any]
            the core layout information dictionary.
        """
        nxblk = wres // wblk
        nyblk = hres // hblk

        ans = None
        x_only = (ext_dir == 'x')
        if x_only or (ext_dir == 'y'):
            # only extend X or Y direction
            if x_only:
                bin_iter = BinaryIterator(nxblk, nxblk + max_blk_ext + 1)
            else:
                bin_iter = BinaryIterator(nyblk, nyblk + max_blk_ext + 1)
            while bin_iter.has_next():
                ncur = bin_iter.get_next()
                if x_only:
                    wcur, hcur = ncur * wblk, hres
                else:
                    wcur, hcur = wres, ncur * hblk
                tmp = self.get_core_info(grid, wcur, hcur, **params)
                if tmp is None:
                    bin_iter.up()
                else:
                    ans = tmp
                    bin_iter.save()
                    bin_iter.down()

            if ans is None:
                raise ValueError(
                    'failed to find DRC clean core with maximum %d '
                    'additional block pitches.' % max_blk_ext)
            if x_only:
                nxblk = bin_iter.get_last_save()
            else:
                nyblk = bin_iter.get_last_save()
            return nxblk, nyblk, ans
        else:
            # extend in both direction
            opt_area = (nxblk + max_blk_ext + 1) * (nyblk + max_blk_ext + 1)
            # linear search in height, binary search in width
            # in this way, for same area, use height as tie breaker
            nxopt, nyopt = nxblk, nyblk
            for nycur in range(nyblk, nyblk + max_blk_ext + 1):
                # check if we should terminate linear search
                if nycur * nxblk >= opt_area:
                    break
                bin_iter = BinaryIterator(nxblk, nxblk + max_blk_ext + 1)
                hcur = nycur * hblk
                while bin_iter.has_next():
                    nxcur = bin_iter.get_next()
                    if nxcur * nycur >= opt_area:
                        # this point can't beat current optimum
                        bin_iter.down()
                    else:
                        tmp = self.get_core_info(grid, nxcur * wblk, hcur,
                                                 **params)
                        if tmp is None:
                            bin_iter.up()
                        else:
                            # found new optimum
                            ans, nxopt, nyopt = tmp, nxcur, nycur
                            opt_area = nxcur * nycur
                            bin_iter.down()

            if ans is None:
                raise ValueError(
                    'failed to find DRC clean core with maximum %d '
                    'additional block pitches.' % max_blk_ext)
            return nxopt, nyopt, ans
示例#12
0
    def size_unit_block(
            self,
            conn_layer: int,
            top_layer: int,
            nx: int,
            ny: int,
            tr_manager: TrackManager,
            wire_specs: Mapping[int, Any],
            mode: ExtendMode,
            max_ext: int = 1000,
            **kwargs: Any
    ) -> Tuple[int, int, Dict[int, WireLookup], ArrayLayInfo]:
        wire_specs = WireSpecs.make_wire_specs(
            conn_layer,
            top_layer,
            tr_manager,
            wire_specs,
            min_size=self.min_size,
            blk_pitch=self.blk_pitch,
            align_default=Alignment.CENTER_COMPACT)

        blk_info: Optional[ArrayLayInfo] = None
        w_min, h_min = wire_specs.min_size
        blk_w, blk_h = wire_specs.blk_size
        w = w_min
        h = h_min
        opt_area = COORD_MAX**2

        def helper_fun(
            w_test: int, h_test: int, binfo: Optional[ArrayLayInfo], wc: int,
            hc: int, opt_a: int, iterator: BinaryIterator
        ) -> Tuple[Optional[ArrayLayInfo], int, int, int]:
            cur_area = w_test * h_test
            if cur_area >= opt_a:
                # this point can't beat current optimum
                iterator.down()
                return binfo, wc, hc, opt_a
            else:
                cur_info = self.get_blk_info(conn_layer, w_test, h_test, nx,
                                             ny, **kwargs)
                if cur_info is None:
                    iterator.up()
                    return binfo, wc, hc, opt_a
                else:
                    # found new optimum
                    iterator.down()
                    return cur_info, w_test, h_test, cur_area

        if mode is ExtendMode.AREA:
            # extend in both direction
            # linear search in height, binary search in width
            # in this way, for same area, use height as tie breaker
            for h_cur in range(h_min, h_min + max_ext * blk_h, blk_h):
                if w_min * h_cur >= opt_area:
                    # terminate linear search
                    break
                bin_iter = BinaryIterator(w_min,
                                          w_min + max_ext * blk_w,
                                          step=blk_w)
                while bin_iter.has_next():
                    w_cur = bin_iter.get_next()
                    blk_info, w, h, opt_area = helper_fun(
                        w_cur, h_cur, blk_info, w, h, opt_area, bin_iter)
        elif mode is ExtendMode.X:
            h_cur = h_min
            bin_iter = BinaryIterator(w_min,
                                      w_min + max_ext * blk_w,
                                      step=blk_w)
            while bin_iter.has_next():
                w_cur = bin_iter.get_next()
                blk_info, w, h, opt_area = helper_fun(w_cur, h_cur, blk_info,
                                                      w, h, opt_area, bin_iter)
        else:
            w_cur = w_min
            bin_iter = BinaryIterator(h_min,
                                      h_min + max_ext * blk_h,
                                      step=blk_h)
            while bin_iter.has_next():
                h_cur = bin_iter.get_next()
                blk_info, w, h, opt_area = helper_fun(w_cur, h_cur, blk_info,
                                                      w, h, opt_area, bin_iter)

        if blk_info is None:
            raise ValueError(f'Failed to find legal resistor unit block size '
                             f'with max_ext={max_ext}')

        return w, h, wire_specs.place_wires(tr_manager, w, h), blk_info
示例#13
0
    async def _design_output_inverter(self, inv_in_pseg: int, inv_in_nseg: int,
                                      pseg: int, nseg: int, inv_nseg: int,
                                      inv_pseg: int, out_inv_m: int,
                                      fanout: float, pinfo: Any,
                                      tbm_specs: Dict[str, Any], has_rst, vin,
                                      vout) -> int:
        """
        Given all other sizes and total output inverter segments, this function will optimize the output inverter
        to minimize rise/fall mismatch.
        """
        tb_params = self._get_full_tb_params()
        # Use a binary iterator to find the PMOS size
        iterator = BinaryIterator(-out_inv_m + 1, out_inv_m - 1)
        err_best = float('inf')
        all_corners = get_tech_global_info(
            'bag3_digital')['signoff_envs']['all_corners']

        while iterator.has_next():
            pseg_off = iterator.get_next()
            dut_params = self._get_lvl_shift_params_dict(pinfo,
                                                         pseg,
                                                         nseg,
                                                         inv_pseg,
                                                         inv_nseg,
                                                         inv_in_nseg,
                                                         inv_in_pseg,
                                                         out_inv_m,
                                                         has_rst,
                                                         dual_output=False,
                                                         skew_out=True,
                                                         out_pseg_off=pseg_off)
            dut = await self.async_new_dut('lvshift', STDCellWrapper,
                                           dut_params)

            err_worst = -1 * float('Inf')
            worst_env = ''
            sim_worst = None
            for env in all_corners['envs']:
                tbm_specs['sim_envs'] = [env]
                tbm_specs['sim_params']['vdd_in'] = all_corners[vin][env]
                tbm_specs['sim_params']['vdd'] = all_corners[vout][env]
                tbm = cast(CombLogicTimingTB,
                           self.make_tbm(CombLogicTimingTB, tbm_specs))
                sim_results = await self.async_simulate_tbm_obj(
                    f'sim_output_inv_pseg_{pseg_off}_{env}', dut, tbm,
                    tb_params)
                tdr_cur, tdf_cur = CombLogicTimingTB.get_output_delay(
                    sim_results.data,
                    tbm.specs,
                    'in',
                    'out',
                    False,
                    in_pwr='vdd_in',
                    out_pwr='vdd')

                if math.isinf(np.max(tdr_cur)) or math.isinf(np.max(tdf_cur)):
                    raise ValueError("Got infinite delay!")
                if tdr_cur[0] < 0 or tdf_cur[0] < 0:
                    raise ValueError("Got negative delay.")

                err_cur = np.abs(tdr_cur[0] - tdf_cur[0])
                if err_cur > err_worst:
                    err_worst = err_cur
                    worst_env = env
                    tdr = tdr_cur[0]
                    tdf = tdf_cur[0]
                    sim_worst = sim_results
            '''
            print(f'iter: {pseg_off}')
            print(f'env: {worst_env}, tdr: {tdr}, tdf: {tdf}')
            breakpoint()
            '''

            if tdr < tdf:
                iterator.down(tdr - tdf)
            else:
                iterator.up(tdr - tdf)

            err_abs = np.abs(tdr - tdf)
            if err_abs < err_best:
                err_best = err_abs
                iterator.save_info(pseg_off)

        pseg_off = iterator.get_last_save_info()
        if pseg_off is None:
            raise ValueError("Could not find PMOS size to match target delay")

        self.log(f'Calculated output inverter to skew PMOS by {pseg_off}.')

        return pseg_off
示例#14
0
    async def _design_lvl_shift_inv_pun(self, pseg: int, nseg: int,
                                        inv_nseg: int, out_inv_m: int,
                                        fanout: float, pinfo: Any,
                                        tbm_specs: Dict[str, Any], has_rst,
                                        dual_output, vin,
                                        vout) -> Tuple[int, int]:
        """
        Given the NMOS pull down size, this function will design the PMOS pull up so that the delay
        mismatch is minimized.
        # TODO: Need to double check on how this handles corners
        """
        inv_beta = get_tech_global_info('bag3_digital')['inv_beta']
        tb_params = self._get_full_tb_params()
        # Use a binary iterator to find the PMOS size
        load_seg = nseg + (pseg if has_rst else 0)
        inv_pseg_nom = int(
            np.round(inv_beta * load_seg / ((1 + inv_beta) * fanout)))
        inv_pseg_nom = 1 if inv_pseg_nom == 0 else inv_pseg_nom
        iterator = BinaryIterator(-inv_pseg_nom + 1, 0)
        err_best = float('inf')
        inv_in_nseg, inv_in_pseg = self._size_input_inv_for_fanout(
            inv_pseg_nom, inv_nseg, pseg, nseg, fanout, has_rst)
        all_corners = get_tech_global_info(
            'bag3_digital')['signoff_envs']['all_corners']

        while iterator.has_next():
            pseg_off = iterator.get_next()
            inv_pseg = inv_pseg_nom + pseg_off
            dut_params = self._get_lvl_shift_params_dict(
                pinfo, pseg, nseg, inv_pseg, inv_nseg, inv_in_nseg,
                inv_in_pseg, out_inv_m, has_rst, dual_output)
            dut = await self.async_new_dut('lvshift', STDCellWrapper,
                                           dut_params)

            err_worst = -1 * float('Inf')
            for env in all_corners['envs']:
                tbm_specs['sim_envs'] = [env]
                tbm_specs['sim_params']['vdd_in'] = all_corners[vin][env]
                tbm_specs['sim_params']['vdd'] = all_corners[vout][env]
                tbm = cast(CombLogicTimingTB,
                           self.make_tbm(CombLogicTimingTB, tbm_specs))
                sim_results = await self.async_simulate_tbm_obj(
                    f'sim_inv_pseg_{inv_pseg}_{env}', dut, tbm, tb_params)
                tdr_cur, tdf_cur = CombLogicTimingTB.get_output_delay(
                    sim_results.data,
                    tbm.specs,
                    'in',
                    'out',
                    False,
                    in_pwr='vdd_in',
                    out_pwr='vdd')
                '''
                plt.figure()
                plt.plot(sim_results.data['time'].flatten(), sim_results.data['in'].flatten(), 'b')
                plt.plot(sim_results.data['time'].flatten(), sim_results.data['inb_buf'].flatten(), 'g')
                plt.plot(sim_results.data['time'].flatten(), sim_results.data['in_buf'].flatten(), 'r')
                plt.plot(sim_results.data['time'].flatten(), sim_results.data['midn'].flatten(), 'k')
                plt.plot(sim_results.data['time'].flatten(), sim_results.data['midp'].flatten(), 'c')
                plt.plot(sim_results.data['time'].flatten(), sim_results.data['out'].flatten(), 'm')
                plt.legend(['in', 'inb_buf', 'in_buf', 'midn', 'midp', 'out'])
                plt.title(f'pseg_off: {pseg_off}, pseg: {inv_pseg}, nseg: {inv_nseg-pseg_off}, fanout: {fanout}')
                plt.show(block=False)
                '''

                # Error checking
                if math.isinf(np.max(tdr_cur)) or math.isinf(np.max(tdf_cur)):
                    raise ValueError("Got infinite delay!")
                if np.min(tdr_cur) < 0 or np.min(tdf_cur) < 0:
                    raise ValueError("Got negative delay.")

                err_cur = np.abs(tdr_cur[0] - tdf_cur[0])
                if err_cur > err_worst:
                    err_worst = err_cur
                    worst_env = env
                    tdr = tdr_cur[0]
                    tdf = tdf_cur[0]
            '''
            print(f'iter: {inv_pseg}')
            print(f'env: {worst_env}, tdr: {tdr}, tdf: {tdf}')
            breakpoint()
            '''

            if tdr < tdf:
                iterator.down(tdr - tdf)
            else:
                iterator.up(tdr - tdf)

            err_abs = np.abs(tdr - tdf)
            if err_abs < err_best:
                err_best = err_abs
                iterator.save_info(pseg_off)

        pseg_off = iterator.get_last_save_info()
        pseg_off = 0 if pseg_off is None else pseg_off  # Should only hit this case if inv_pseg_nom = 1
        inv_pseg = inv_pseg_nom + pseg_off

        return inv_pseg, inv_nseg - 0 * pseg_off
示例#15
0
    def draw_layout(self):
        # type: () -> None
        lch = self.params['lch']
        ptap_w = self.params['ptap_w']
        ntap_w = self.params['ntap_w']
        wp = self.params['wp']
        wn = self.params['wn']
        thp = self.params['thp']
        thn = self.params['thn']
        nx = self.params['nx']
        ny = self.params['ny']
        fill_config = self.params['fill_config']
        top_layer = self.params['top_layer']
        sup_width = self.params['sup_width']
        options = self.params['options']
        show_pins = self.params['show_pins']

        if options is None:
            options = {}

        # get power fill size
        w_tot, h_tot = self.grid.get_fill_size(top_layer, fill_config, unit_mode=True)
        w_tot *= nx
        h_tot *= ny
        # get number of fingers
        info = AnalogBaseInfo(self.grid, lch, 0, top_layer=top_layer)
        bin_iter = BinaryIterator(2, None)
        while bin_iter.has_next():
            fg_cur = bin_iter.get_next()
            w_cur = info.get_placement_info(fg_cur).tot_width
            if w_cur < w_tot:
                bin_iter.save()
                bin_iter.up()
            elif w_cur > w_tot:
                bin_iter.down()
            else:
                bin_iter.save()
                break

        fg_tot = bin_iter.get_last_save()
        if fg_tot is None:
            raise ValueError('Decaep cell width exceed fill width.')
        self.draw_base(lch, fg_tot, ptap_w, ntap_w, [wn], [thn], [wp], [thp],
                       ng_tracks=[1], pg_tracks=[1], n_orientations=['MX'],
                       p_orientations=['R0'], top_layer=top_layer, min_height=h_tot,
                       **options)

        if self.bound_box.height_unit > h_tot:
            raise ValueError('Decap cell height exceed fill height.')

        nmos = self.draw_mos_conn('nch', 0, 0, fg_tot, 0, 0)
        pmos = self.draw_mos_conn('pch', 0, 0, fg_tot, 2, 2, gate_pref_loc='s')

        vss_tid = self.make_track_id('pch', 0, 'g', 0)
        vdd_tid = self.make_track_id('nch', 0, 'g', 0)

        self.connect_to_substrate('ptap', nmos['d'])
        self.connect_to_substrate('ntap', pmos['s'])
        vss_g = self.connect_to_tracks([nmos['s'], pmos['g']], vss_tid)
        vdd_g = self.connect_to_tracks([pmos['d'], nmos['g']], vdd_tid)

        vss, vdd = self.fill_dummy(vdd_width=sup_width, vss_width=sup_width)
        vss.append(vss_g)
        vdd.append(vdd_g)
        self.add_pin('VSS', vss, label='VSS:', show=show_pins)
        self.add_pin('VDD', vdd, label='VDD:', show=show_pins)
示例#16
0
def design_tail(specs, itarg, seg_min):
    """Find smallest tail transistor that biases the differential amplifier."""
    db = specs['in_db']
    sim_env = specs['sim_env']
    vds = specs['vdst_min']
    in_type = specs['in_type']
    
    if in_type == 'pch':
        vds *= -1

    ib_fun = db.get_function('ibias', env=sim_env)
    vgs_idx = db.get_fun_arg_index('vgs')
    vgs_min, vgs_max = ib_fun.get_input_range(vgs_idx)

    # binary search on number of fingers.
    seg_tail_iter = BinaryIterator(seg_min, None, step=2)
    while seg_tail_iter.has_next():
        seg_tail = seg_tail_iter.get_next()

        def fun_zero(vgs):
            farg = db.get_fun_arg(vgs=vgs, vds=vds, vbs=0)
            return ib_fun(farg) * seg_tail - itarg

        val_min = fun_zero(vgs_min)
        val_max = fun_zero(vgs_max)
        if val_min > 0 and val_max > 0:
            # smallest possible current > itarg
            seg_tail_iter.down()
        elif val_min < 0 and val_max < 0:
            # largest possbile current < itarg
            seg_tail_iter.up()
        else:
            vbias = sciopt.brentq(fun_zero, vgs_min, vgs_max)  # type: float
            seg_tail_iter.save_info(vbias)
            seg_tail_iter.down()

    seg_tail = seg_tail_iter.get_last_save()
    if seg_tail is None:
        raise ValueError('No solution for tail.')
    vgs_opt = seg_tail_iter.get_last_save_info()

    tail_op = db.query(vbs=0, vds=vds, vgs=vgs_opt)
    return seg_tail, tail_op
示例#17
0
def find_tail_bias(fun_ibiasn, nch_db, vtail, vgs_min, vgs_max, seg_tail_min,
                   itarg):
    seg_tail_iter = BinaryIterator(seg_tail_min, None, step=2)
    while seg_tail_iter.has_next():
        seg_tail = seg_tail_iter.get_next()

        def fun_zero(vgs):
            narg = nch_db.get_fun_arg(vgs=vgs, vds=vtail, vbs=0)
            return fun_ibiasn(narg) * seg_tail - itarg

        if fun_zero(vgs_min) > 0:
            # smallest possible current > itarg
            seg_tail_iter.down()
        if fun_zero(vgs_max) < 0:
            # largest possible current < itarg
            seg_tail_iter.up()
        else:
            vbias = sciopt.brentq(fun_zero, vgs_min, vgs_max)  # type: float
            seg_tail_iter.save_info(vbias)
            seg_tail_iter.down()

    seg_tail = seg_tail_iter.get_last_save()
    vbias = seg_tail_iter.get_last_save_info()

    return seg_tail, vbias
示例#18
0
    def draw_layout(self):
        # type: () -> None
        w = self.params['w']
        h_unit = self.params['h_unit']
        narr = self.params['narr']
        sub_type = self.params['sub_type']
        threshold = self.params['threshold']
        top_layer = self.params['top_layer']
        nser = self.params['nser']
        ndum = self.params['ndum']
        port_tr_w = self.params['port_tr_w']
        res_type = self.params['res_type']
        res_options = self.params['res_options']
        cap_spx = self.params['cap_spx']
        cap_spy = self.params['cap_spy']
        cap_h_list = self.params['cap_h_list']
        half_blk_x = self.params['half_blk_x']
        show_pins = self.params['show_pins']

        if nser % 2 != 0:
            raise ValueError('This generator only supports even nser.')

        res = self.grid.resolution
        lay_unit = self.grid.layout_unit
        w_unit = int(round(w / lay_unit / res))

        if res_options is None:
            my_options = dict(well_end_mode=2)
        else:
            my_options = res_options.copy()
            my_options['well_end_mode'] = 2
        # find resistor length
        info = ResArrayBaseInfo(self.grid,
                                sub_type,
                                threshold,
                                top_layer=top_layer,
                                res_type=res_type,
                                ext_dir='y',
                                options=my_options,
                                connect_up=True,
                                half_blk_x=half_blk_x,
                                half_blk_y=True)

        lmin, lmax = info.get_res_length_bounds()
        bin_iter = BinaryIterator(lmin, lmax, step=2)
        while bin_iter.has_next():
            lcur = bin_iter.get_next()
            htot = info.get_place_info(lcur, w_unit, 1, 1)[3]
            if htot < h_unit:
                bin_iter.save()
                bin_iter.up()
            else:
                bin_iter.down()

        # draw resistor
        l_unit = bin_iter.get_last_save()
        nx = 2 * ndum + narr * nser
        self.draw_array(l_unit * lay_unit * res,
                        w,
                        sub_type,
                        threshold,
                        nx=nx,
                        ny=1,
                        top_layer=top_layer,
                        res_type=res_type,
                        grid_type=None,
                        ext_dir='y',
                        options=my_options,
                        connect_up=True,
                        half_blk_x=half_blk_x,
                        half_blk_y=True,
                        min_height=h_unit)

        # get cap settings
        bot_layer = self.bot_layer_id + 1
        for lay in range(bot_layer, top_layer + 1):
            if self.grid.get_direction(lay) == 'x':
                cap_spx = max(
                    cap_spx,
                    self.grid.get_line_end_space(lay, 1, unit_mode=True))

        # connect resistors and draw MOM caps
        tmp = self._connect_resistors(narr, nser, ndum, cap_spx, port_tr_w,
                                      show_pins)
        rout_list, cap_x_list = tmp
        tmp = self._draw_mom_cap(cap_x_list, bot_layer, top_layer, cap_spy,
                                 cap_h_list, port_tr_w, show_pins)
        cout_list, ores_info, cres_info = tmp

        # connect bias resistor to cap
        for rout, cout in zip(rout_list, cout_list):
            self.connect_to_track_wires(rout, cout)

        # set schematic parameters
        self._sch_params = dict(
            narr=narr,
            ndum=ndum * 2,
            hp_params=dict(
                l=l_unit * lay_unit * res,
                w=w,
                intent=res_type,
                nser=nser,
                ndum=0,
                res_in_info=cres_info,
                res_out_info=ores_info,
                sub_name='VSS',
            ),
        )
示例#19
0
    async def _upsize_gate_for_del_spec(
        self,
        dut_params: Dict[str, Any],
        tspec: float,
        seg_cur: int,
        is_nand: bool,
        tbm: CombLogicTimingTB,
        seg_even: bool,
        spec_type: str,
        seg_max: Optional[int] = None,
    ) -> Tuple[int, np.ndarray, np.ndarray]:
        if spec_type != 'delay' and spec_type != 'slope':
            raise ValueError("spec_type must be either 'delay' or 'slope'.")

        bin_iter = BinaryIterator(seg_cur, seg_max, step=1 << seg_even)
        while bin_iter.has_next():
            new_seg = bin_iter.get_next()
            dut_params['params'][
                'seg_nand' if is_nand else 'seg_nor'] = new_seg
            dut = await self.async_new_dut('nand_nor_upsize', STDCellWrapper,
                                           dut_params)
            sim_results = await self.async_simulate_tbm_obj(
                'nand_nor_upsize_sim', dut, tbm, self._tb_params)
            if spec_type == 'slope':
                ans = CombLogicTimingTB.get_output_trf(
                    sim_results.data, tbm.specs,
                    'nand_pu' if is_nand else 'nor_pd')
                gate_tr, gate_tf = ans
            else:
                ans = CombLogicTimingTB.get_output_delay(
                    sim_results.data,
                    tbm.specs,
                    'in',
                    'nand_pu' if is_nand else 'nor_pd',
                    out_invert=True)
                gate_tf, gate_tr = ans

            trf_metric = gate_tf if is_nand else gate_tr
            if np.max(trf_metric) > tspec:
                bin_iter.up(np.max(trf_metric) - tspec)
            else:
                bin_iter.down(np.max(trf_metric) - tspec)
                bin_iter.save_info((new_seg, gate_tr, gate_tf))

        info = bin_iter.get_last_save_info()
        if info is None:
            gate_str = "nand" if is_nand else "nor"
            err_str = f'Could not find a size for {gate_str} to meet the target spec of {tspec}.'
            self.error(err_str)
        seg, tr, tf = info

        return seg, tr, tf
示例#20
0
    def draw_layout(self):
        # type: () -> None
        w = self.params['w']
        h_unit = self.params['h_unit']
        sub_type = self.params['sub_type']
        threshold = self.params['threshold']
        top_layer = self.params['top_layer']
        nser = self.params['nser']
        ndum = self.params['ndum']
        in_tr_info = self.params['in_tr_info']
        out_tr_info = self.params['out_tr_info']
        bias_idx = self.params['bias_idx']
        vdd_tr_info = self.params['vdd_tr_info']
        res_type = self.params['res_type']
        res_options = self.params['res_options']
        cap_spx = self.params['cap_spx']
        cap_spy = self.params['cap_spy']
        cap_margin = self.params['cap_margin']
        half_blk_x = self.params['half_blk_x']
        show_pins = self.params['show_pins']

        res = self.grid.resolution
        lay_unit = self.grid.layout_unit
        w_unit = int(round(w / lay_unit / res))

        if res_options is None:
            my_options = dict(well_end_mode=2)

        else:
            my_options = res_options.copy()
            my_options['well_end_mode'] = 2
        # find resistor length
        info = ResArrayBaseInfo(self.grid,
                                sub_type,
                                threshold,
                                top_layer=top_layer,
                                res_type=res_type,
                                grid_type=None,
                                ext_dir='y',
                                options=my_options,
                                connect_up=True,
                                half_blk_x=half_blk_x,
                                half_blk_y=True)

        lmin, lmax = info.get_res_length_bounds()
        bin_iter = BinaryIterator(lmin, lmax, step=2)
        while bin_iter.has_next():
            lcur = bin_iter.get_next()
            htot = info.get_place_info(lcur, w_unit, 1, 1)[3]
            if htot < h_unit:
                bin_iter.save()
                bin_iter.up()
            else:
                bin_iter.down()

        # draw resistor
        l_unit = bin_iter.get_last_save()
        nx = 2 * (nser + ndum)
        self.draw_array(l_unit * lay_unit * res,
                        w,
                        sub_type,
                        threshold,
                        nx=nx,
                        ny=1,
                        top_layer=top_layer,
                        res_type=res_type,
                        grid_type=None,
                        ext_dir='y',
                        options=my_options,
                        connect_up=True,
                        half_blk_x=half_blk_x,
                        half_blk_y=True,
                        min_height=h_unit)
        # connect resistors
        vdd, biasp, biasn, outp_h, outn_h, xl, xr = self.connect_resistors(
            ndum, nser, bias_idx)
        # draw MOM cap
        caplp, capln, caprp, caprn = self.draw_mom_cap(nser, xl, xr, cap_spx,
                                                       cap_spy, cap_margin)

        # connect resistors to MOM cap, and draw metal resistors
        vm_layer = self.bot_layer_id + 1
        self.connect_to_tracks(outp_h, capln.track_id)
        self.connect_to_tracks(outn_h, caprn.track_id)

        # connect outputs to horizontal tracks
        hm_layer = vm_layer + 1
        pidx, nidx, tr_w = in_tr_info
        res_in_w = self.grid.get_track_width(hm_layer, tr_w, unit_mode=True)
        inp, inn = self.connect_differential_tracks(caplp,
                                                    caprp,
                                                    hm_layer,
                                                    pidx,
                                                    nidx,
                                                    width=tr_w)
        tr_lower, tr_upper = inp.lower_unit, inp.upper_unit
        self.add_res_metal_warr(hm_layer,
                                pidx,
                                tr_lower - res_in_w,
                                tr_lower,
                                width=tr_w,
                                unit_mode=True)
        self.add_res_metal_warr(hm_layer,
                                nidx,
                                tr_lower - res_in_w,
                                tr_lower,
                                width=tr_w,
                                unit_mode=True)
        inp = self.add_wires(hm_layer,
                             pidx,
                             tr_lower - 2 * res_in_w,
                             tr_lower - res_in_w,
                             width=tr_w,
                             unit_mode=True)
        inn = self.add_wires(hm_layer,
                             nidx,
                             tr_lower - 2 * res_in_w,
                             tr_lower - res_in_w,
                             width=tr_w,
                             unit_mode=True)
        pidx, nidx, tr_w = out_tr_info
        res_out_w = self.grid.get_track_width(hm_layer, tr_w, unit_mode=True)
        self.connect_differential_tracks(capln,
                                         caprn,
                                         hm_layer,
                                         pidx,
                                         nidx,
                                         track_lower=tr_lower,
                                         track_upper=tr_upper,
                                         width=tr_w,
                                         unit_mode=True)
        self.add_res_metal_warr(hm_layer,
                                pidx,
                                tr_upper,
                                tr_upper + res_out_w,
                                width=tr_w,
                                unit_mode=True)
        self.add_res_metal_warr(hm_layer,
                                nidx,
                                tr_upper,
                                tr_upper + res_out_w,
                                width=tr_w,
                                unit_mode=True)
        outp = self.add_wires(hm_layer,
                              pidx,
                              tr_upper + res_out_w,
                              tr_upper + 2 * res_out_w,
                              width=tr_w,
                              unit_mode=True)
        outn = self.add_wires(hm_layer,
                              nidx,
                              tr_upper + res_out_w,
                              tr_upper + 2 * res_out_w,
                              width=tr_w,
                              unit_mode=True)
        # connect/export vdd
        if vdd_tr_info is None:
            self.add_pin('VDD_vm', vdd, label='VDD:', show=show_pins)
        else:
            self.add_pin('VDD_vm', vdd, label='VDD', show=show_pins)
            for tr_info in vdd_tr_info:
                tid = TrackID(hm_layer, tr_info[0], width=tr_info[1])
                self.add_pin('VDD',
                             self.connect_to_tracks(vdd, tid),
                             show=show_pins)
        # add pins
        self.add_pin('biasp', biasp, show=show_pins)
        self.add_pin('biasn', biasn, show=show_pins)
        self.add_pin('outp', outp, show=show_pins)
        self.add_pin('outn', outn, show=show_pins)
        self.add_pin('inp', inp, show=show_pins)
        self.add_pin('inn', inn, show=show_pins)

        self._sch_params = dict(
            l=l_unit * lay_unit * res,
            w=w,
            intent=res_type,
            nser=nser,
            ndum=ndum,
            res_in_info=(hm_layer, res_in_w * res * lay_unit,
                         res_in_w * res * lay_unit),
            res_out_info=(hm_layer, res_out_w * res * lay_unit,
                          res_out_w * res * lay_unit),
        )
    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
    def design(
            self,
            i1_unit,  # type: List[float]
            i1_min_size,  # type: int
            vg_list,  # type: List[float]
            vout_list,  # type: List[float]
            cpar1,  # type: float
            cload,  # type: float
            f_unit,  # type: float
            phase_margin,  # type: float
            res_var,  # type: float
            l,  # type: float
            vstar_gm_min,  # type: float
            ft_load_scale,  # type: float
            vds_tail_min,  # type: float
            seg_gm_min,  # type: int
            vdd,  # type: float
            pmos_input=True,  # type: bool
            max_ref_ratio=20,  # type: int
            load_stack_list=None,  # type: Optional[List[int]]
    ):
        # type: (...) -> None

        # binary search for minimum stage 1 current,
        i1_size_iter = BinaryIterator(i1_min_size, None)
        i1_size_opt, opt_info = None, None
        while i1_size_iter.has_next():
            i1_size = i1_size_iter.get_next()
            print('trying i1_size = %d' % i1_size)
            try:
                self._design_with_itarg(i1_size, i1_unit, vg_list, vout_list,
                                        cpar1, cload, f_unit, phase_margin,
                                        res_var, l, vstar_gm_min,
                                        ft_load_scale, vds_tail_min,
                                        seg_gm_min, vdd, pmos_input,
                                        max_ref_ratio, load_stack_list)
                success = True
            except StageOneCurrentError as err:
                print(err)
                success = False

            if success:
                print('success')
                opt_info = self._amp_info
                i1_size_opt = i1_size
                i1_size_iter.down()
            else:
                i1_size_iter.up()

        # linear search to find optimal scale2
        scale2_int_max = int(opt_info['scale2'])
        if scale2_int_max == opt_info['scale2']:
            scale2_int_max -= 1

        last_i1_size = i1_size_opt
        print('i1_size = %d, scale2 = %.4g' %
              (i1_size_opt, opt_info['scale2']))
        for scale2_test in range(scale2_int_max, 0, -1):
            i1_size_test = int(
                np.floor(i1_size_opt * (1 + opt_info['scale2']) /
                         (1 + scale2_test)))
            if i1_size_test <= last_i1_size or scale2_test == opt_info[
                    'scale2']:
                continue
            print('testing i1_size = %d, scale2 = %.4g' %
                  (i1_size_test, scale2_test))
            try:
                self._design_with_itarg(i1_size_test, i1_unit, vg_list,
                                        vout_list, cpar1, cload, f_unit,
                                        phase_margin, res_var, l, vstar_gm_min,
                                        ft_load_scale, vds_tail_min,
                                        seg_gm_min, vdd, pmos_input,
                                        max_ref_ratio, load_stack_list)
            except StageOneCurrentError as err:
                print(err)
                continue
            if self._amp_info['scale2'] <= scale2_test:
                # found new minimum.  close in to find optimal i1 size
                opt_info = self._amp_info
                i1_size_opt = i1_size_test
                print('update: i1_size = %d, scale2 = %.4g' %
                      (i1_size_opt, opt_info['scale2']))
                i1_size_iter = BinaryIterator(last_i1_size + 1, i1_size_test)
                while i1_size_iter.has_next():
                    i1_size_cur_opt = i1_size_iter.get_next()
                    print('testing i1_size = %d' % i1_size_cur_opt)
                    try:
                        self._design_with_itarg(i1_size_cur_opt, i1_unit,
                                                vg_list, vout_list, cpar1,
                                                cload, f_unit, phase_margin,
                                                res_var, l, vstar_gm_min,
                                                ft_load_scale, vds_tail_min,
                                                seg_gm_min, vdd, pmos_input,
                                                max_ref_ratio, load_stack_list)

                        if self._amp_info['scale2'] <= opt_info['scale2']:
                            opt_info = self._amp_info
                            i1_size_opt = i1_size_cur_opt
                            print('update: i1_size = %d, '
                                  'scale2 = %.4g' %
                                  (i1_size_opt, opt_info['scale2']))
                            i1_size_iter.down()
                        else:
                            i1_size_iter.up()

                    except StageOneCurrentError as err:
                        print(err)
                        i1_size_iter.up()

            last_i1_size = i1_size_test

        self._amp_info = opt_info
示例#23
0
def characterize_casc_amp(env_list,
                          fg_list,
                          w_list,
                          db_list,
                          vbias_list,
                          vload_list,
                          vtail_list,
                          vmid_list,
                          vcm,
                          vdd,
                          vin_max,
                          cw,
                          rw,
                          fanout,
                          ton,
                          k_settle_targ,
                          verr_max,
                          scale_res=0.1,
                          scale_min=0.25,
                          scale_max=20):
    # compute DC transfer function curve and compute linearity spec
    results = solve_casc_diff_dc(env_list,
                                 db_list,
                                 w_list,
                                 fg_list,
                                 vbias_list,
                                 vload_list,
                                 vtail_list,
                                 vmid_list,
                                 vdd,
                                 vcm,
                                 vin_max,
                                 verr_max,
                                 num_points=20)

    vin_vec, vmat_list, verr_list, gain_list = results

    # compute settling ratio
    fg_in, fg_casc, fg_load = fg_list[1:]
    db_in, db_casc, db_load = db_list[1:]
    w_in, w_casc, w_load = w_list[1:]
    fzin = 1.0 / (2 * ton)
    wzin = 2 * np.pi * fzin
    tvec = np.linspace(0, ton, 200, endpoint=True)
    scale_list = []
    for env, vload, vtail, vmid in zip(env_list, vload_list, vtail_list,
                                       vmid_list):
        # step 1: construct half circuit
        in_params = db_in.query(env=env,
                                w=w_in,
                                vbs=-vtail,
                                vds=vmid - vtail,
                                vgs=vcm - vtail)
        casc_params = db_casc.query(env=env,
                                    w=w_casc,
                                    vbs=-vmid,
                                    vds=vcm - vmid,
                                    vgs=vdd - vmid)
        load_params = db_load.query(env=env,
                                    w=w_load,
                                    vbs=0,
                                    vds=vcm - vdd,
                                    vgs=vload - vdd)
        circuit = LTICircuit()
        circuit.add_transistor(in_params, 'mid', 'in', 'gnd', fg=fg_in)
        circuit.add_transistor(casc_params, 'd', 'gnd', 'mid', fg=fg_casc)
        circuit.add_transistor(load_params, 'd', 'gnd', 'gnd', fg=fg_load)
        # step 2: get input capacitance
        zin = circuit.get_impedance('in', fzin)
        cin = (1 / zin).imag / wzin
        circuit.add_cap(cin * fanout, 'out', 'gnd')
        # step 3: find scale factor to achieve k_settle
        bin_iter = BinaryIterator(scale_min,
                                  None,
                                  step=scale_res,
                                  is_float=True)
        while bin_iter.has_next():
            # add scaled wired parasitics
            cur_scale = bin_iter.get_next()
            cap_cur = cw / 2 / cur_scale
            res_cur = rw * cur_scale
            circuit.add_cap(cap_cur, 'd', 'gnd')
            circuit.add_cap(cap_cur, 'out', 'gnd')
            circuit.add_res(res_cur, 'd', 'out')
            # get settling factor
            sys = circuit.get_voltage_gain_system('in', 'out')
            dc_gain = sys.freqresp(w=np.array([0.1]))[1][0]
            sgn = 1 if dc_gain.real >= 0 else -1
            dc_gain = abs(dc_gain)
            _, yvec = scipy.signal.step(
                sys, T=tvec)  # type: Tuple[np.ndarray, np.ndarray]
            k_settle_cur = 1 - abs(yvec[-1] - sgn * dc_gain) / dc_gain
            print('scale = %.4g, k_settle = %.4g' % (cur_scale, k_settle_cur))
            # update next scale factor
            if k_settle_cur >= k_settle_targ:
                print('save scale = %.4g' % cur_scale)
                bin_iter.save()
                bin_iter.down()
            else:
                if cur_scale > scale_max:
                    raise ValueError(
                        'cannot meet settling time spec at scale = %d' %
                        cur_scale)
                bin_iter.up()
            # remove wire parasitics
            circuit.add_cap(-cap_cur, 'd', 'gnd')
            circuit.add_cap(-cap_cur, 'out', 'gnd')
            circuit.add_res(-res_cur, 'd', 'out')
        scale_list.append(bin_iter.get_last_save())

    return vmat_list, verr_list, gain_list, scale_list
    def _design_stage2(self, gm_db, load_db, vtail_list, vg_list, vmid_list,
                       vout_list, vbias_list, vb_gm, vb_load, cload, cpar1,
                       w_dict, th_dict, stack_dict, seg_dict, gm2_list,
                       res_var, phase_margin, f_unit, max_ref_ratio):

        seg_tail1 = seg_dict['tail1']
        seg_diode1 = seg_dict['diode1']
        seg_ngm1 = seg_dict['ngm1']

        # step 1: find stage 2 unit size
        seg_gcd = gcd(gcd(seg_tail1, seg_diode1), seg_ngm1)
        if seg_gcd % 2 != 0:
            raise ValueError('All segment numbers must be even.')
        # divide seg_gcd by 2 to make sure all generated segment numbers are even
        seg_gcd //= 2

        # make sure we have enough tail fingers for common mode feedback
        min_size = 2 if seg_tail1 // seg_gcd == 2 else 1

        def ac_results_fun(cur_size):
            seg_dict['tail2'] = seg_tail1 // seg_gcd * cur_size
            seg_dict['diode2'] = seg_diode1 // seg_gcd * cur_size
            seg_dict['ngm2'] = seg_ngm1 // seg_gcd * cur_size
            cur_scale2 = cur_size / seg_gcd

            cur_gm2_list = [gm2 * cur_scale2 for gm2 in gm2_list]
            ac_results = self._find_rz_cf(gm_db, load_db, vtail_list, vg_list,
                                          vmid_list, vout_list, vbias_list,
                                          vb_gm, vb_load, cload, cpar1, w_dict,
                                          th_dict, stack_dict, seg_dict,
                                          cur_gm2_list, res_var, phase_margin)

            return ac_results

        def funity_fun(cur_size):
            ac_results_tmp = ac_results_fun(cur_size)
            fu_list = ac_results_tmp[0]
            if fu_list is None:
                return -1
            # noinspection PyTypeChecker
            ans = min(fu_list)
            return ans

        # find min_size such that amplifier is stable
        min_bin_iter = BinaryIterator(min_size, None)
        while min_bin_iter.has_next():
            test_size = min_bin_iter.get_next()
            test_fu = funity_fun(test_size)
            if test_fu >= 0:
                min_bin_iter.save()
                min_bin_iter.down()
            else:
                min_bin_iter.up()

        min_result = minimize_cost_golden(funity_fun,
                                          f_unit,
                                          offset=min_bin_iter.get_last_save())

        if min_result.x is None:
            msg = 'Insufficient stage 1 current.  funity_max=%.4g'
            raise StageOneCurrentError(msg % min_result.vmax)

        funity_list, rz_nom, cf_min, gain_list, f3db_list, pm_list = ac_results_fun(
            min_result.x)

        seg_tail2_tot = seg_dict['tail2']
        seg_tail2 = (seg_tail2_tot // 4) * 2
        seg_tailcm = seg_tail2_tot - seg_tail2
        seg_tail_tot = 2 * (seg_dict['tail1'] + seg_tail2)
        seg_dict['tail2'] = seg_tail2
        seg_dict['tailcm'] = seg_tailcm
        seg_dict['ref'] = max(2, -((-seg_tail_tot // max_ref_ratio) // 2) * 2)
        return dict(
            rz=rz_nom,
            cf=cf_min,
            gain=gain_list,
            f_3db=f3db_list,
            f_unity=funity_list,
            phase_margin=pm_list,
        )
示例#25
0
def fill_symmetric_min_density_info(area: int,
                                    n_min: int,
                                    n_max: int,
                                    sp_min: int,
                                    area_specs: List[Tuple[int, int, int]],
                                    sp_max: Optional[int] = None,
                                    fill_on_edge: bool = True,
                                    cyclic: bool = False) -> FillInfo:
    """Fill the given 1-D area to satisfy minimum density constraint

    Compute fill location such that the given area is filled with the following properties:

    1. the area is as uniform as possible.
    2. the area is symmetric with respect to the center
    3. all fill blocks have lengths between n_min and n_max.
    4. all fill blocks are at least sp_min apart.

    Parameters
    ----------
    area : int
        total number of space we need to fill.
    n_min : int
        minimum length of the fill block.  Must be less than or equal to n_max.
    n_max : int
        maximum length of the fill block.
    sp_min : int
        minimum space between each fill block.
    area_specs : List[Tuple[int, int, int]]
        list of area specifications, in (target, scale, extension) format.
    sp_max : Optional[int]
        if given, make sure space between blocks does not exceed this value.
        Must be greater than sp_min
    fill_on_edge : bool
        If True, we put fill blocks on area boundary.  Otherwise, we put space block on
        area boundary.
    cyclic : bool
        If True, we assume we're filling in a cyclic area (it wraps around).

    Returns
    -------
    info : FillInfo
        the fill information object.
    """
    # first, fill as much as possible using scale/extension of the first area spec.
    max_result = fill_symmetric_max_density_info(area,
                                                 n_min,
                                                 n_max,
                                                 sp_min,
                                                 area_specs,
                                                 sp_max=sp_max,
                                                 fill_on_edge=fill_on_edge,
                                                 cyclic=cyclic)

    if not max_result.meet_area_specs(area_specs):
        # we cannot meet area spec; return max result
        return max_result

    # now, reduce fill by doing binary search on n_max
    nfill_opt = max_result.num_fill
    n_max_iter = BinaryIterator(n_min, n_max)
    while n_max_iter.has_next():
        n_max_cur = n_max_iter.get_next()
        try:
            info = fill_symmetric_max_num_info(area,
                                               nfill_opt,
                                               n_min,
                                               n_max_cur,
                                               sp_min,
                                               fill_on_edge=fill_on_edge,
                                               cyclic=cyclic)
            if info.meet_area_specs(area_specs) and (sp_max is None
                                                     or info.sp_max <= sp_max):
                # both specs passed
                n_max_iter.save_info(info)
                n_max_iter.down()
            else:
                # reduce n_max too much
                n_max_iter.up()

        except ValueError:
            # get here if n_min == n_max and there's no solution.
            n_max_iter.up()

    last_save = n_max_iter.get_last_save_info()
    if last_save is None:
        # no solution, return max result
        return max_result
    else:
        max_result = last_save

    # see if we can further reduce fill by doing binary search on nfill_opt
    nfill_iter = BinaryIterator(1, nfill_opt)
    n_max = n_max_iter.get_last_save()
    while nfill_iter.has_next():
        nfill_cur = nfill_iter.get_next()
        try:
            info = fill_symmetric_max_num_info(area,
                                               nfill_cur,
                                               n_min,
                                               n_max,
                                               sp_min,
                                               fill_on_edge=fill_on_edge,
                                               cyclic=cyclic)
            if info.meet_area_specs(area_specs) and (sp_max is None
                                                     or info.sp_max <= sp_max):
                # both specs passed
                nfill_iter.save_info(info)
                nfill_iter.down()
            else:
                # reduce nfill too much
                nfill_iter.up()

        except ValueError:
            nfill_iter.up()

    last_save = nfill_iter.get_last_save_info()
    if last_save is None:
        return max_result
    # return new minimum solution
    return last_save
    def _find_min_cfb(cls, phase_margin, results):
        axis_names = ['corner', 'cfb']

        corner_list = results['corner']
        corner_sort_arg = np.argsort(corner_list)  # type: Sequence[int]

        # rearrange array axis
        sweep_vars = results['sweep_params']['pm_vout']
        order = [sweep_vars.index(name) for name in axis_names]
        pm_data = np.transpose(results['pm_vout'], axes=order)

        # determine minimum cfb
        cfb_vec = results['cfb']
        cfb_idx_min = 0
        for corner_idx in corner_sort_arg:
            bin_iter = BinaryIterator(cfb_idx_min, cfb_vec.size)
            while bin_iter.has_next():
                cur_cfb_idx = bin_iter.get_next()
                pm = pm_data[corner_idx, cur_cfb_idx]
                if pm >= phase_margin:
                    bin_iter.save()
                    bin_iter.down()
                else:
                    bin_iter.up()
            cfb_idx_min = bin_iter.get_last_save()
            if cfb_idx_min is None:
                # No solution; cannot make amplifier stable
                break

        if cfb_idx_min is None:
            raise ValueError('Cannot determine cfb.')
        else:
            cfb = cfb_vec[cfb_idx_min]

        return cfb.item()
示例#27
0
文件: grid.py 项目: zhaokai-l/bag
    def get_max_track_width(self,
                            layer_id: int,
                            num_tracks: int,
                            tot_space: int,
                            half_end_space: bool = False) -> int:
        """Compute maximum track width and space that satisfies DRC rule.

        Given available number of tracks and numbers of tracks needed, returns
        the maximum possible track width.

        Parameters
        ----------
        layer_id : int
            the track layer ID.
        num_tracks : int
            number of tracks to draw.
        tot_space : int
            available number of tracks.
        half_end_space : bool
            True if end spaces can be half of minimum spacing.  This is true if you're
            these tracks will be repeated, or there are no adjacent tracks.

        Returns
        -------
        tr_w : int
            track width.
        """
        bin_iter = BinaryIterator(1, None)
        while bin_iter.has_next():
            tr_w = bin_iter.get_next()
            tr_sep = self.get_sep_tracks(layer_id, tr_w, tr_w)
            if half_end_space:
                used_tracks = tr_sep * num_tracks
            else:
                used_tracks = tr_sep * (num_tracks -
                                        1) + 2 * self.get_sep_tracks(
                                            layer_id, tr_w, 1)
            if used_tracks > tot_space:
                bin_iter.down()
            else:
                bin_iter.save()
                bin_iter.up()

        opt_w = bin_iter.get_last_save()
        return opt_w
示例#28
0
    def draw_layout(self):
        # type: () -> None

        top_layer = self.params['top_layer']
        lch = self.params['lch']
        w = self.params['w']
        sub_type = self.params['sub_type']
        threshold = self.params['threshold']
        port_width = self.params['port_width']
        well_width = self.params['well_width']
        end_mode = self.params['end_mode']
        is_passive = self.params['is_passive']
        max_nxblk = self.params['max_nxblk']
        port_tid = self.params['port_tid']
        show_pins = self.params['show_pins']

        half_blk_y = self.params['half_blk_y']
        half_blk_x = self.params['half_blk_x']

        res = self.grid.resolution
        well_width = int(round(well_width / res))
        right_end = (end_mode & 8) != 0
        left_end = (end_mode & 4) != 0
        top_end = (end_mode & 2) != 0
        bot_end = (end_mode & 1) != 0

        # get layout info, also set RoutingGrid to substrate grid.
        layout_info = AnalogBaseInfo(self.grid,
                                     lch,
                                     0,
                                     top_layer=top_layer,
                                     end_mode=end_mode,
                                     half_blk_y=half_blk_y,
                                     half_blk_x=half_blk_x)
        # compute template width in number of sd pitches
        # find maximum number of fingers we can draw
        bin_iter = BinaryIterator(1, None)
        while bin_iter.has_next():
            cur_fg = bin_iter.get_next()
            cur_pinfo = layout_info.get_placement_info(cur_fg)
            cur_core_width = cur_pinfo.core_width
            if cur_core_width == well_width:
                bin_iter.save_info(cur_pinfo)
                break
            elif cur_core_width < well_width:
                bin_iter.save_info(cur_pinfo)
                bin_iter.up()
            else:
                bin_iter.down()

        sub_fg_tot = bin_iter.get_last_save()
        if sub_fg_tot is None:
            raise ValueError('Cannot draw substrate that fit in width: %d' %
                             well_width)

        # check width parity requirement
        if max_nxblk > 0:
            blkw = self.grid.get_block_size(top_layer, unit_mode=True)[0]
            place_info = bin_iter.get_last_save_info()
            cur_nxblk = place_info.tot_width // blkw
            while sub_fg_tot > 0 and (cur_nxblk > max_nxblk or
                                      (max_nxblk - cur_nxblk) % 2 != 0):
                sub_fg_tot -= 1
                place_info = layout_info.get_placement_info(sub_fg_tot)
                cur_nxblk = place_info.tot_width // blkw
            if sub_fg_tot <= 0:
                raise ValueError('Cannot draw substrate with width = %d, '
                                 'max_nxblk = %d' % (well_width, max_nxblk))

        layout_info.set_fg_tot(sub_fg_tot)
        self.grid = layout_info.grid

        place_info = layout_info.get_placement_info(sub_fg_tot)
        edgel_x0 = place_info.edge_margins[0]
        tot_width = place_info.tot_width

        # create masters
        master_list = [
            self.new_template(params=dict(
                lch=lch,
                fg=sub_fg_tot,
                sub_type=sub_type,
                threshold=threshold,
                is_end=bot_end,
                top_layer=top_layer,
            ),
                              temp_cls=AnalogEndRow),
            self.new_template(params=dict(
                lch=lch,
                w=w,
                sub_type=sub_type,
                threshold=threshold,
                fg=sub_fg_tot,
                top_layer=top_layer,
                options=dict(is_passive=is_passive),
            ),
                              temp_cls=AnalogSubstrate),
            self.new_template(params=dict(
                lch=lch,
                fg=sub_fg_tot,
                sub_type=sub_type,
                threshold=threshold,
                is_end=top_end,
                top_layer=top_layer,
            ),
                              temp_cls=AnalogEndRow),
        ]

        ycur = 0
        array_box = BBox.get_invalid_bbox()
        sub_conn, inst = None, None
        for master, orient in zip(master_list, ['R0', 'R0', 'MX']):
            if orient == 'MX':
                ycur += master.array_box.top_unit

            name_id = master.get_layout_basename()
            edge_layout_info = master.get_edge_layout_info()
            xcur = edgel_x0
            if left_end:
                edge_info = master.get_left_edge_info()
                edge_params = dict(
                    is_end=True,
                    guard_ring_nf=0,
                    name_id=name_id,
                    layout_info=edge_layout_info,
                    adj_blk_info=edge_info,
                )
                edge_master = self.new_template(params=edge_params,
                                                temp_cls=AnalogEdge)
                if not edge_master.is_empty:
                    edge_inst = self.add_instance(edge_master,
                                                  loc=(edgel_x0, ycur),
                                                  orient=orient,
                                                  unit_mode=True)
                    array_box = array_box.merge(edge_inst.array_box)
                    xcur = edge_inst.array_box.right_unit

            inst = self.add_instance(master,
                                     loc=(xcur, ycur),
                                     orient=orient,
                                     unit_mode=True)
            array_box = array_box.merge(inst.array_box)
            if isinstance(master, AnalogSubstrate):
                conn_params = dict(
                    layout_info=edge_layout_info,
                    layout_name=name_id + '_subconn',
                    is_laygo=False,
                )
                conn_master = self.new_template(params=conn_params,
                                                temp_cls=AnalogSubstrateConn)
                sub_conn = self.add_instance(conn_master,
                                             loc=(xcur, ycur),
                                             orient=orient,
                                             unit_mode=True)
            xcur = inst.array_box.right_unit

            if right_end:
                edge_info = master.get_right_edge_info()
                edge_params = dict(
                    is_end=True,
                    guard_ring_nf=0,
                    name_id=name_id,
                    layout_info=edge_layout_info,
                    adj_blk_info=edge_info,
                )
                edge_master = self.new_template(params=edge_params,
                                                temp_cls=AnalogEdge)
                if not edge_master.is_empty:
                    xcur += edge_master.array_box.right_unit
                    eor = 'MY' if orient == 'R0' else 'R180'
                    edge_inst = self.add_instance(edge_master,
                                                  loc=(xcur, ycur),
                                                  orient=eor,
                                                  unit_mode=True)
                    array_box = array_box.merge(edge_inst.array_box)

            if orient == 'R0':
                ycur += master.array_box.top_unit

        # calculate substrate Y coordinates
        imp_yb, thres_yb = master_list[0].sub_ysep
        imp_yt, thres_yt = master_list[2].sub_ysep
        self._sub_bndy = (imp_yb, ycur - imp_yt), (thres_yb, ycur - thres_yt)

        # get left/right substrate coordinates
        tot_imp_box = BBox.get_invalid_bbox()
        for lay in self.grid.tech_info.get_implant_layers('ptap'):
            tot_imp_box = tot_imp_box.merge(self.get_rect_bbox(lay))
        for lay in self.grid.tech_info.get_implant_layers('ntap'):
            tot_imp_box = tot_imp_box.merge(self.get_rect_bbox(lay))

        if not tot_imp_box.is_physical():
            self._sub_bndx = None, None
        else:
            self._sub_bndx = tot_imp_box.left_unit, tot_imp_box.right_unit

        # set array box and size
        self.array_box = array_box
        bound_box = BBox(0,
                         0,
                         tot_width,
                         inst.bound_box.top_unit,
                         res,
                         unit_mode=True)
        if self.grid.size_defined(top_layer):
            self.set_size_from_bound_box(top_layer, bound_box)
        else:
            self.prim_bound_box = bound_box
            self.prim_top_layer = top_layer

        hm_layer = layout_info.mconn_port_layer + 1
        if port_tid is None:
            # find center track index
            hm_mid = self.grid.coord_to_nearest_track(hm_layer,
                                                      self.array_box.yc_unit,
                                                      mode=0,
                                                      half_track=True,
                                                      unit_mode=True)
            # connect to horizontal metal layer.
            hm_pitch = self.grid.get_track_pitch(hm_layer, unit_mode=True)
            ntr = self.array_box.height_unit // hm_pitch  # type: int
            if port_width is None:
                port_width = self.grid.get_max_track_width(
                    hm_layer, 1, ntr, half_end_space=False)
            port_tid = TrackID(hm_layer, hm_mid, width=port_width)
        else:
            port_tid = TrackID(hm_layer, port_tid[0], width=port_tid[1])

        port_name = 'VDD' if sub_type == 'ntap' else 'VSS'
        sub_wires = self.connect_to_tracks(
            sub_conn.get_port(port_name).get_pins(hm_layer - 1), port_tid)
        self.add_pin(port_name, sub_wires, show=show_pins)

        self._fg_tot = sub_fg_tot
示例#29
0
def fill_symmetric_min_density_info(area,
                                    targ_area,
                                    n_min,
                                    n_max,
                                    sp_min,
                                    sp_max=None,
                                    fill_on_edge=True,
                                    cyclic=False):
    # type: (int, int, int, int, int, Optional[int], bool, bool) -> Tuple[Tuple[Any, ...], bool]
    """Fill the given 1-D area as little as possible.

    Compute fill location such that the given area is filled with the following properties:

    1. the area is as uniform as possible.
    2. the area is symmetric with respect to the center
    3. all fill blocks have lengths between n_min and n_max.
    4. all fill blocks are at least sp_min apart.

    Parameters
    ----------
    area : int
        total number of space we need to fill.
    targ_area : int
        target minimum fill area.  If not achievable, will do the best that we can.
    n_min : int
        minimum length of the fill block.  Must be less than or equal to n_max.
    n_max : int
        maximum length of the fill block.
    sp_min : int
        minimum space between each fill block.
    sp_max : Optional[int]
        if given, make sure space between blocks does not exceed this value.
        Must be greater than sp_min
    fill_on_edge : bool
        If True, we put fill blocks on area boundary.  Otherwise, we put space block on
        area boundary.
    cyclic : bool
        If True, we assume we're filling in a cyclic area (it wraps around).

    Returns
    -------
    info : Tuple[Any, ...]
        the fill information tuple.
    invert : bool
        True if space/fill is inverted.
    """
    # first, fill as much as possible
    max_result = fill_symmetric_max_density_info(area,
                                                 targ_area,
                                                 n_min,
                                                 n_max,
                                                 sp_min,
                                                 sp_max=sp_max,
                                                 fill_on_edge=fill_on_edge,
                                                 cyclic=cyclic)

    fill_area, nfill_opt = max_result[0][:2]
    if fill_area <= targ_area:
        # we cannot/barely meet area spec; return max result
        return max_result

    # now, reduce fill by doing binary search on n_max
    n_max_iter = BinaryIterator(n_min, n_max)
    while n_max_iter.has_next():
        n_max_cur = n_max_iter.get_next()
        try:
            info, invert = fill_symmetric_max_num_info(
                area,
                nfill_opt,
                n_min,
                n_max_cur,
                sp_min,
                fill_on_edge=fill_on_edge,
                cyclic=cyclic)
            fill_area_cur = area - info[0] if invert else info[0]
            if invert:
                _, sp_cur = _get_min_max_blk_len(info)
            else:
                sp_cur = sp_min if info[1][2] == 0 else sp_min + 1
            if fill_area_cur >= targ_area and (sp_max is None
                                               or sp_cur <= sp_max):
                # both specs passed
                n_max_iter.save_info((info, invert))
                n_max_iter.down()
            else:
                # reduce n_max too much
                n_max_iter.up()

        except ValueError:
            # get here if n_min == n_max and there's no solution.
            n_max_iter.up()

    last_save = n_max_iter.get_last_save_info()
    if last_save is None:
        # no solution, return max result
        return max_result

    # return new minimum solution
    info, invert = last_save
    fill_area = area - info[0] if invert else info[0]
    return (fill_area, nfill_opt, info[1]), invert
示例#30
0
    def draw_layout(self):
        lch = self.params['lch']
        ntap_w = self.params['ntap_w']
        w = self.params['w']
        fg_ref = self.params['fg_ref']
        threshold = self.params['threshold']
        out_xc_list = self.params['out_xc_list']
        supply_tracks = self.params['supply_tracks']
        em_specs = self.params['em_specs']
        tr_widths = self.params['tr_widths']
        tr_spaces = self.params['tr_spaces']
        tot_width = self.params['tot_width']
        guard_ring_nf = self.params['guard_ring_nf']
        show_pins = self.params['show_pins']

        # get AnalogBaseInfo
        hm_layer = self.mos_conn_layer + 1
        ym_layer = hm_layer + 1
        layout_info = AnalogBaseInfo(self.grid,
                                     lch,
                                     guard_ring_nf,
                                     top_layer=ym_layer,
                                     half_blk_y=False)

        # compute total number of fingers to achieve target width.
        bin_iter = BinaryIterator(2, None, step=2)
        while bin_iter.has_next():
            fg_cur = bin_iter.get_next()
            w_cur = layout_info.get_placement_info(fg_cur).tot_width
            if w_cur < tot_width:
                bin_iter.save()
                bin_iter.up()
            elif w_cur > tot_width:
                bin_iter.down()
            else:
                bin_iter.save()
                break

        fg_tot = bin_iter.get_last_save()
        # find number of tracks needed for output tracks from EM specs
        hm_tr_w_out = self.grid.get_min_track_width(hm_layer, **em_specs)
        hm_tr_sp_out = self.grid.get_num_space_tracks(hm_layer,
                                                      hm_tr_w_out,
                                                      half_space=True)
        hm_w = self.grid.get_track_width(hm_layer, hm_tr_w_out, unit_mode=True)
        ym_tr_w = self.grid.get_min_track_width(ym_layer,
                                                bot_w=hm_w,
                                                **em_specs,
                                                unit_mode=True)

        # construct track width/space dictionary from EM specs
        tr_manager = TrackManager(self.grid,
                                  tr_widths,
                                  tr_spaces,
                                  half_space=True)
        tr_w_dict = {
            'in': {
                hm_layer: tr_manager.get_width(hm_layer, 'in')
            },
            'out': {
                hm_layer: hm_tr_w_out,
                ym_tr_w: ym_tr_w
            },
        }
        tr_sp_dict = {
            ('in', 'out'): {
                hm_layer:
                max(hm_tr_sp_out, tr_manager.get_space(hm_layer,
                                                       ('in', 'out')))
            },
        }
        tr_manager = TrackManager(self.grid,
                                  tr_w_dict,
                                  tr_sp_dict,
                                  half_space=True)

        pw_list = [w, w, w]
        pth_list = [threshold, threshold, threshold]
        wire_names = dict(
            nch=[],
            pch=[
                dict(
                    ds=['out'],
                    g=['in'],
                ),
                dict(
                    g=['out'],
                    ds=['out'],
                    ds2=['out'],
                ),
                dict(g=['in'], ds=['out']),
            ],
        )
        # draw transistor rows
        self.draw_base(lch,
                       fg_tot,
                       ntap_w,
                       ntap_w, [], [],
                       pw_list,
                       pth_list,
                       tr_manager=tr_manager,
                       wire_names=wire_names,
                       p_orientations=['MX', 'R0', 'R0'],
                       guard_ring_nf=guard_ring_nf,
                       pgr_w=ntap_w,
                       ngr_w=ntap_w,
                       top_layer=ym_layer,
                       half_blk_y=False)

        outn_tid = self.get_wire_id('pch', 0, 'ds', wire_name='out')
        inp_tid = self.get_wire_id('pch', 0, 'g', wire_name='in')
        bias_tid = self.get_wire_id('pch', 1, 'g', wire_name='out')
        vdd_tid = self.get_wire_id('pch', 1, 'ds', wire_name='out')
        tail_tid = self.get_wire_id('pch', 1, 'ds2', wire_name='out')
        inn_tid = self.get_wire_id('pch', 2, 'g', wire_name='in')
        outp_tid = self.get_wire_id('pch', 2, 'ds', wire_name='out')

        out_pitch = out_xc_list[1] - out_xc_list[0]
        sd_pitch = layout_info.sd_pitch_unit
        if out_pitch % sd_pitch != 0:
            raise ValueError('Oops')
        fg = out_pitch // sd_pitch - fg_ref

        # draw transistors and connect
        inp_list = []
        inn_list = []
        tail_list = []
        bias_list = []
        vdd_m_list = []
        outp_list = []
        outn_list = []
        layout_info = self.layout_info
        num_out = len(out_xc_list)
        for idx, xc in enumerate(out_xc_list):
            ym_idx = self.grid.coord_to_track(ym_layer, xc, unit_mode=True)
            vtid = TrackID(ym_layer, ym_idx, width=ym_tr_w)
            # find column index that centers on given track index
            x_coord = self.grid.track_to_coord(ym_layer,
                                               ym_idx,
                                               unit_mode=True)
            col_center = layout_info.coord_to_col(x_coord, unit_mode=True)
            col_idx = col_center - (fg // 2)
            # draw transistors
            if idx == 0:
                mref = self.draw_mos_conn('pch',
                                          1,
                                          col_idx - fg_ref,
                                          fg_ref,
                                          2,
                                          0,
                                          d_net='ibias',
                                          diode_conn=True,
                                          gate_pref_loc='d')
                bias_list.append(mref['g'])
                bias_list.append(mref['d'])
                vdd_m_list.append(mref['s'])

            mtop = self.draw_mos_conn('pch',
                                      2,
                                      col_idx,
                                      fg,
                                      2,
                                      0,
                                      s_net='ioutp',
                                      d_net='tail')
            mbot = self.draw_mos_conn('pch',
                                      0,
                                      col_idx,
                                      fg,
                                      0,
                                      2,
                                      s_net='ioutn',
                                      d_net='tail')
            mtail = self.draw_mos_conn('pch',
                                       1,
                                       col_idx,
                                       fg,
                                       2,
                                       0,
                                       gate_pref_loc='s',
                                       s_net='',
                                       d_net='tail')
            mref = self.draw_mos_conn('pch',
                                      1,
                                      col_idx + fg,
                                      fg_ref,
                                      2,
                                      0,
                                      gate_pref_loc='d',
                                      diode_conn=True,
                                      d_net='ibias')
            # connect
            inp_list.append(mbot['g'])
            inn_list.append(mtop['g'])
            bias_list.append(mref['g'])
            bias_list.append(mref['d'])
            bias_list.append(mtail['g'])
            tail_list.append(mtop['d'])
            tail_list.append(mbot['d'])
            tail_list.append(mtail['d'])
            vdd_m_list.append(mtail['s'])
            vdd_m_list.append(mref['s'])

            outp_h = self.connect_to_tracks(mtop['s'], outp_tid)
            outp_list.append(outp_h)
            self.add_pin('ioutp',
                         self.connect_to_tracks(outp_h, vtid),
                         show=show_pins)
            outn_h = self.connect_to_tracks(mbot['s'], outn_tid)
            outn_list.append(outn_h)
            self.add_pin('ioutn',
                         self.connect_to_tracks(outn_h, vtid),
                         show=show_pins)

        self.connect_wires(outp_list)
        self.connect_wires(outn_list)
        self.add_pin('inp',
                     self.connect_to_tracks(inp_list,
                                            inp_tid,
                                            track_lower=0,
                                            unit_mode=True),
                     show=show_pins)
        self.add_pin('inn',
                     self.connect_to_tracks(inn_list,
                                            inn_tid,
                                            track_lower=0,
                                            unit_mode=True),
                     show=show_pins)
        ibias = self.connect_to_tracks(bias_list,
                                       bias_tid,
                                       track_lower=0,
                                       unit_mode=True)
        self.add_pin('ibias', ibias, show=show_pins)
        self.connect_to_tracks(tail_list,
                               tail_tid,
                               track_lower=ibias.lower_unit,
                               track_upper=ibias.upper_unit,
                               unit_mode=True)
        vdd_m = self.connect_to_tracks(vdd_m_list, vdd_tid)

        _, vdd_warrs = self.fill_dummy()
        vdd_warrs.append(vdd_m)
        right_tidx = 0
        for tidx in supply_tracks:
            vtid = TrackID(ym_layer, tidx, width=ym_tr_w)
            right_tidx = max(right_tidx, tidx)
            self.add_pin('VDD',
                         self.connect_to_tracks(vdd_warrs, vtid),
                         show=show_pins)
        for xc in out_xc_list:
            tidx = self.grid.coord_to_track(ym_layer, xc, unit_mode=True)
            vtid = TrackID(ym_layer, tidx, width=ym_tr_w)
            self.add_pin('VDD',
                         self.connect_to_tracks(vdd_m, vtid),
                         show=show_pins)

        self.fill_box = bnd_box = self.bound_box
        for lay in range(1, self.top_layer):
            self.do_max_space_fill(lay, bnd_box, fill_pitch=3)

        self._sch_params = dict(
            lch=lch,
            w_dict={
                'in': w,
                'tail': w
            },
            th_dict={
                'in': threshold,
                'tail': threshold
            },
            seg_dict={
                'in': fg * num_out,
                'tail': fg * num_out,
                'ref': fg_ref * (num_out + 1)
            },
            dum_info=self.get_sch_dummy_info(),
        )
        ratio = fg * num_out / (fg_ref * (num_out + 1))
        scale = num_out / ratio
        self._ibias_em_specs = em_specs.copy()
        for key in ['idc', 'iac_rms', 'iac_peak']:
            if key in em_specs:
                self._ibias_em_specs[key] *= scale