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
Beispiel #2
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
Beispiel #3
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
Beispiel #4
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
Beispiel #5
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
Beispiel #6
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,
        )
Beispiel #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,
    )
Beispiel #9
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
Beispiel #10
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
Beispiel #11
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
Beispiel #12
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
Beispiel #13
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.
        """
        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
        inv_nseg_nom = inv_nseg  # save the value of nseg coming into this function as nominal
        range_to_vary = min(inv_pseg_nom, inv_nseg)
        iterator = BinaryIterator(
            -range_to_vary + 1,
            1)  # upper limit is exclusive hence using 1 instead of 0
        # variation will be done such that the total P+N segments remains the same
        # This allows the input inverter sizing to be done once at the start

        # iterator = BinaryIterator(-inv_pseg_nom+1, 0)
        # iterator = BinaryIteratorInterval(get_tech_global_info('bag3_digital')['width_interval_list_p'],
        #                                   -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
            inv_nseg = inv_nseg_nom - pseg_off
            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_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')
                print("Balance rise/fall delays inv_pup Info: ", env, tdr_cur,
                      tdf_cur)
                # 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]

            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
        inv_nseg = inv_nseg_nom - pseg_off

        return inv_pseg, inv_nseg - 0 * pseg_off