Exemple #1
0
    def _get_unit_cell_params(self,
                              pinfo: Any,
                              seg_p: int,
                              seg_n: int,
                              seg_nand: int,
                              seg_nor: int,
                              nand_p_w_del: int,
                              nor_n_w_del: int,
                              beta_ratio: Optional[float] = None,
                              w_min: Optional[int] = None) -> Dict[str, Any]:
        if w_min is None:
            w_n = get_tech_global_info('aib_ams')['w_minn']
        else:
            w_n = w_min

        if beta_ratio is None:
            beta_ratio = get_tech_global_info('aib_ams')['inv_beta']

        return dict(cls_name='aib_ams.layout.driver.OutputDriverCore',
                    draw_taps=True,
                    params=dict(
                        pinfo=pinfo,
                        seg_p=seg_p,
                        seg_n=seg_n,
                        seg_nand=seg_nand,
                        seg_nor=seg_nor,
                        w_p=self.out_w_p,
                        w_n=self.out_w_n,
                        w_p_nand=int(beta_ratio * w_n) + nand_p_w_del,
                        w_n_nand=w_n,
                        w_p_nor=int(beta_ratio * w_n),
                        w_n_nor=w_n + nor_n_w_del,
                        export_pins=True,
                    ))
Exemple #2
0
    def _size_input_inv_for_fanout(inv_pseg: int, inv_nseg: int, pseg: int,
                                   nseg: int, fanout: float,
                                   has_rst: bool) -> Tuple[int, int]:
        beta = get_tech_global_info('bag3_digital')['inv_beta']
        seg_load = inv_pseg + inv_nseg + nseg + (pseg if has_rst else 0)
        iinv_nseg = int(np.round(seg_load / (1 + beta) / fanout))
        iinv_nseg = 1 if iinv_nseg < get_tech_global_info(
            'bag3_digital')['seg_min'] else iinv_nseg
        iinv_pseg = int(np.round(seg_load * beta / (1 + beta) / fanout))
        iinv_pseg = 1 if iinv_pseg < get_tech_global_info(
            'bag3_digital')['seg_min'] else iinv_pseg

        return iinv_nseg, iinv_pseg
 async def verify_design(self, dut_params: Mapping[str, Any]) -> Mapping[str, Any]:
     dut = await self.async_new_dut('phase_det', GenericWrapper, dut_params, flat=True)
     offset_netlist = Path(*dut.netlist_path.parts[:-1], 'netlist_with_offsets.scs')
     v_offset_map = add_mismatch_offsets(dut.netlist_path, offset_netlist, self._sim_db._sim.netlist_type)
     designed_dut_with_offsets = DesignInstance(dut.cell_name, dut.sch_master, dut.lay_master,
                                                offset_netlist, dut.cv_info_list)
     seg_dict: Mapping[str, Any] = dut_params['params']['flop_params']['sa_params']['seg_dict']
     global_params = get_tech_global_info("aib_ams")
     a_vt_per_fin = global_params['A_vt_fin_n']
     seg_in = seg_dict['in']
     seg_tail = seg_dict['tail']
     offset_tail = 3*(a_vt_per_fin/seg_tail)**(1/2)
     offset_in = 3*(a_vt_per_fin/seg_in)**(1/2)
     strong_arm_offsets = dict(
         XFLOPD_XSA_XTAIL=-offset_tail,
         XFLOPU_XSA_XTAIL=-offset_tail,
         XFLOPD_XSA_XINP=offset_in,
         XFLOPD_XSA_XINN=-offset_in,
         XFLOPU_XSA_XINP=offset_in,
         XFLOPU_XSA_XINN=-offset_in,
     )
     for mos_name, voff_name in v_offset_map.items():
         found = False
         for base_name, offset in strong_arm_offsets.items():
             if mos_name.startswith(base_name):
                 self._phase_det_mm_params['tbm_specs']['sim_params'][voff_name] = offset
                 found = True
         if not found:
             self._phase_det_mm_params['tbm_specs']['sim_params'][voff_name] = 0
     mm = self.make_mm(PhaseDetMeasManager, self._phase_det_mm_params)
     results = await self.async_simulate_mm_obj(f'phase_det_timing_with_offset', designed_dut_with_offsets, mm)
     return results.data
Exemple #4
0
    def _build_env_vars(env_str: str, vin: str,
                        vout: str) -> Tuple[List[str], float, float]:
        dsn_env_info = get_tech_global_info(
            'bag3_digital')['dsn_envs'][env_str]
        design_sim_env = dsn_env_info['env']
        vdd_in = dsn_env_info[vin]
        vdd_out = dsn_env_info[vout]

        return design_sim_env, vdd_in, vdd_out
Exemple #5
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.global_info = get_tech_global_info('aib_ams')
     lch = self.global_info['lch_min']
     w_p = self.global_info['w_minp']
     w_n = self.global_info['w_minn']
     th = self.global_info['thresholds'][0]
     self._tech_dsn_base_params = dict(lch=lch,
                                       w_p=w_p,
                                       w_n=w_n,
                                       th_p=th,
                                       th_n=th)
Exemple #6
0
    async def _get_resistance(
            self, sim_id: str, dut_params: Mapping[str, Any],
            mm_specs: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]:
        """Compute resistance from DC I-V measurements.
        """
        gen_params = dict(
            cls_name=PullUpDown.get_qualified_name(),
            draw_taps=True,
            params=dut_params,
        )
        dut = await self.async_new_dut('unit_cell',
                                       STDCellWrapper,
                                       gen_params,
                                       flat=True)

        extract = self._sim_db.extract
        netlist_type = DesignOutput.CDL if extract else self._sim_db.prj.sim_access.netlist_type

        # Create a new netlist that allows for the insertion of dc sources
        offset_netlist = Path(
            *dut.netlist_path.parts[:-1],
            f'netlist_with_sources.{netlist_type.extension}')
        v_offset_map = add_internal_sources(dut.netlist_path, offset_netlist,
                                            netlist_type, ['d'])

        mm_specs['v_offset_map'] = v_offset_map
        mm_specs['extract'] = extract

        # Create a DesignInstance with the newly created netlist
        new_dut = DesignInstance(dut.cell_name, dut.sch_master, dut.lay_master,
                                 offset_netlist, dut.cv_info_list)

        all_corners = get_tech_global_info(
            'aib_ams')['signoff_envs']['all_corners']
        res_pd = np.array([])
        res_pu = np.array([])

        helper = GatherHelper()
        for env in all_corners['envs']:
            helper.append(
                self._run_get_resistance(env, sim_id, new_dut, mm_specs,
                                         all_corners))

        results = await helper.gather_err()
        for idx in range(len(results)):
            res_pu = np.append(res_pu, results[idx][0])
            res_pd = np.append(res_pd, results[idx][1])

        return res_pu, res_pd
Exemple #7
0
    def _get_lvl_shift_core_params_dict(
            pinfo: Any,
            seg_p: int,
            seg_n: int,
            has_rst: bool,
            is_ctrl: bool = False) -> Dict[str, Any]:
        """
        Creates a dictionary of parameters for the layout class LevelShifterCore
        seg_n : nmos Pull down nseg
        seg_p : pmos Pull up nseg
        pinfo : pinfo
        Note: This will let the width be passed through the pinfo, currently no rst
        """
        global_info = get_tech_global_info('bag3_digital')
        wn = global_info['w_minn'] if is_ctrl else 2 * global_info['w_minn']
        wp = global_info['w_minp'] if is_ctrl else 2 * global_info['w_minp']

        if has_rst:
            seg_dict = dict(pd=seg_n,
                            pu=seg_p,
                            rst=int(np.ceil(seg_n / 2)),
                            prst=seg_p)
            w_dict = dict(pd=wn, pu=wp, rst=wn)
        else:
            seg_dict = dict(pd=seg_n, pu=seg_p)
            w_dict = dict(pd=wn, pu=wp)
        lv_params = dict(cls_name=LevelShifterCore.get_qualified_name(),
                         draw_taps=True,
                         params=dict(
                             pinfo=pinfo,
                             seg_dict=seg_dict,
                             w_dict=w_dict,
                             has_rst=has_rst,
                             in_upper=has_rst,
                         ))

        if has_rst:
            lv_params['params']['lv_params']['stack_p'] = 2

        return lv_params
Exemple #8
0
    def _design_lvl_shift_core_size(cload: float, k_ratio: float,
                                    inv_input_cap: float, fanout: float,
                                    is_ctrl: bool) -> Tuple[int, int, int]:
        """ Size the core of the LVL Shifter given K_ratio, the ratio of the NMOS to PMOS
        """
        out_inv_input_cap = cload / fanout
        print(f'cload = {cload}')
        inv_m = int(round(out_inv_input_cap / inv_input_cap))
        inv_m = max(1, inv_m)
        beta = get_tech_global_info('bag3_digital')['inv_beta']
        # pseg = int(round(2*inv_m / fanout))
        # total equivalent inv load is inv_m so the PMOS size should be inv_m/fanout times beta
        pseg = int(round(beta * inv_m / fanout))
        pseg = max(1, pseg)
        if pseg == 1 and not is_ctrl:
            print("=" * 80)
            print(
                "WARNING: LvShift Designer: pseg has been set to 1; might want to remove output inverter."
            )
            print("=" * 80)

        nseg = int(np.round(pseg * k_ratio))

        return inv_m, pseg, nseg
Exemple #9
0
    async def async_design(self,
                           num_units: int,
                           num_units_nom: int,
                           num_units_min: int,
                           trf_max: float,
                           r_targ: float,
                           r_min_weak: float,
                           c_ext: float,
                           freq: float,
                           trf_in: float,
                           rel_err: float,
                           del_err: float,
                           tile_name: str,
                           tile_specs: Mapping[str, Any],
                           res_mm_specs: Dict[str, Any],
                           ridx_p: int = 1,
                           ridx_n: int = 1,
                           stack_max: int = 10,
                           tran_options: Optional[Mapping[str, Any]] = None,
                           max_iter: int = 10,
                           **kwargs: Any) -> Mapping[str, Any]:
        """ Design the Output Driver Cell

        1) Calls functions to get initial design for main driver unit cell and weak driver
        2) Characterize design
        3) Iterate on main driver design to account for additional loading from other unit cells

        Parameters
        ----------
        num_units: int
            Number of unit cells. Must be between 3 and 6
        num_units_nom: int
            ???
        num_units_min: int
            Min. number of unit cells
        trf_max: float
            Max. output rise / fall time
        r_targ: float
            Target output resistance
        r_min_weak: float
            Target output resistance for weak driver
        c_ext: float
            Load capacitance
        freq: float
            Operating switching frequency
        trf_in: float
            Input rise / fall time
        rel_err: float
            Output resistance error tolerance, used in DriverPullUpDownDesigner
        del_err: float
            Delay mismatch tolerance, used in DriverUnitCellDesigner for sizing NAND + NOR
        tile_name: str
            Tile name for layout.
        tile_specs: Mapping[str, Any]
            Tile specifications for layout.
        res_mm_specs: Mapping[str, Any]
            MeasurementManager specs for DriverPullUpDownMM, used in DriverPullUpDownDesigner
        ridx_n: int
            NMOS transistor row
        ridx_p: int
            PMOS transistor row
        stack_max: int
            Max. number of stacks possible in pull up / pull down driver
        tran_options: Optional[Mapping[str, Any]]
            Additional transient simulation options dictionary, used in DriverPullUpDownDesigner
        max_iter: int
            Max. number of iterations for final main driver resizing
        kwargs: Any
            Additional keyword arguments. Unused here

        Returns
        -------
        ans: Mapping[str, Any]
            Design summary, including generator parameters and performance summary

        """
        tech_info = get_tech_global_info('aib_ams')
        w_p = tech_info['w_maxp']
        w_n = tech_info['w_maxn']
        tile_specs['place_info'][tile_name]['row_specs'][0]['width'] = w_n
        tile_specs['place_info'][tile_name]['row_specs'][1]['width'] = w_p
        self._dsn_specs['tile_specs']['place_info'][tile_name]['row_specs'][0][
            'width'] = w_n
        self._dsn_specs['tile_specs']['place_info'][tile_name]['row_specs'][1][
            'width'] = w_p
        core_params = dict(
            r_targ=r_targ * num_units,
            stack_max=stack_max,
            trf_max=trf_max,
            c_min=c_ext / num_units,
            c_max=c_ext / num_units_min,
            w_p=w_p,
            w_n=w_n,
            freq=freq,
            vdd=tech_info['dsn_envs']['slow_io']['vddio'],
            vdd_max=tech_info['signoff_envs']['vmax']['vddio'],
            trf_in=trf_in,
            rel_err=rel_err,
            del_err=del_err,
            tile_specs=tile_specs,
            tile_name=tile_name,
            tran_options=tran_options,
            res_mm_specs=res_mm_specs,
        )
        weak_params = core_params.copy()
        weak_params['w_p'] = tech_info['w_minp']
        weak_params['w_n'] = tech_info['w_minn']
        weak_params['r_targ'] = r_min_weak
        weak_params['is_weak'] = True

        # Design weak pull-up/pull-down
        weak_designer = DriverPullUpDownDesigner(self._root_dir, self._sim_db,
                                                 self._dsn_specs)
        weak_designer.set_dsn_specs(weak_params)
        weak_results = await weak_designer.async_design(**weak_params)

        # Design main output driver
        core_designer = DriverUnitCellDesigner(self._root_dir, self._sim_db,
                                               self._dsn_specs)
        core_designer.set_dsn_specs(core_params)
        summary = await core_designer.async_design(**core_params)

        # Characterize the initial design
        result_params = summary['dut_params']['params']
        tinfo_table = TileInfoTable.make_tiles(self.grid, tile_specs)
        pinfo = tinfo_table[tile_name]
        dut_params = dict(cls_name='aib_ams.layout.driver.AIBOutputDriver',
                          draw_taps=True,
                          params=dict(
                              pinfo=pinfo,
                              pupd_params=dict(
                                  seg_p=weak_results['seg_p'],
                                  seg_n=weak_results['seg_n'],
                                  stack=weak_results['stack'],
                                  w_p=weak_results['w_p'],
                                  w_n=weak_results['w_n'],
                              ),
                              unit_params=dict(
                                  seg_p=result_params['seg_p'],
                                  seg_n=result_params['seg_n'],
                                  seg_nand=result_params['seg_nand'],
                                  seg_nor=result_params['seg_nor'],
                                  w_p=result_params['w_p'],
                                  w_n=result_params['w_n'],
                                  w_p_nand=result_params['w_p_nand'],
                                  w_n_nand=result_params['w_n_nand'],
                                  w_p_nor=result_params['w_p_nor'],
                                  w_n_nor=result_params['w_n_nor']),
                              export_pins=True,
                          ))
        tbm_specs: Dict[str, Any] = dict(thres_lo=0.1,
                                         thres_hi=0.9,
                                         tstep=None,
                                         sim_params=dict(
                                             cload=c_ext,
                                             tbit=20 * r_targ * c_ext,
                                             trf=trf_in,
                                         ),
                                         rtol=1e-8,
                                         atol=1e-22,
                                         tran_options=tran_options,
                                         save_outputs=['out', 'in'])

        if num_units == 3:
            n_enb_str = 'VDD'
            p_en_str = 'VSS'
        elif num_units == 4:
            n_enb_str = 'VDD,VSS'
            p_en_str = 'VSS,VDD'
        elif num_units == 5:
            n_enb_str = 'VSS,VDD'
            p_en_str = 'VDD,VSS'
        elif num_units == 6:
            n_enb_str = 'VSS,VSS'
            p_en_str = 'VDD,VDD'
        else:
            raise ValueError('Number of units must be between 3 and 6.')

        tb_params = dict(load_list=[('out', 'cload')],
                         dut_conns={
                             'txpadout': 'out',
                             'din': 'in',
                             'n_enb_drv<1:0>': n_enb_str,
                             'p_en_drv<1:0>': p_en_str,
                             'tristate': 'VSS',
                             'tristateb': 'VDD',
                             'weak_pden': 'VSS',
                             'weak_puenb': 'VDD'
                         })

        all_corners = tech_info['signoff_envs']['all_corners']
        trf_worst = -float('inf')
        tdr_worst = -float('inf')
        tdf_worst = -float('inf')
        tf_worst = -float('inf')
        tr_worst = -float('inf')
        worst_env = ''
        duty_err_worst = 0
        dut = await self.async_new_dut('output_driver', STDCellWrapper,
                                       dut_params)
        for env in all_corners['envs']:
            tbm_specs['sim_envs'] = [env]
            tbm_specs['sim_params']['vdd'] = all_corners['vddio'][env]
            tbm = cast(CombLogicTimingTB,
                       self.make_tbm(CombLogicTimingTB, tbm_specs))
            sim_results = await self.async_simulate_tbm_obj(
                f'sim_final_{env}', dut, tbm, tb_params)
            tdr, tdf = CombLogicTimingTB.get_output_delay(
                sim_results.data, tbm_specs, 'in', 'out', False)

            if tdr[0] > tdr_worst:
                tdr_worst = tdr[0]
            if tdf[0] > tdf_worst:
                tdf_worst = tdf[0]

            duty_err = tdr[0] - tdf[0]
            if np.abs(duty_err) > np.abs(duty_err_worst):
                duty_err_worst = duty_err

            tr, tf = CombLogicTimingTB.get_output_trf(sim_results.data,
                                                      tbm_specs, 'out')
            trf = max(np.max(tr), np.max(tf))

            if trf > trf_worst:
                trf_worst = trf
                tr_worst = tr[0]
                tf_worst = tf[0]
                worst_env = env

        self.logger.info('---')
        self.logger.info("Completed initial design.")
        self.logger.info(f'Target R per segment was: {core_params["r_targ"]}')
        self.logger.info(
            f'Worst trf was: {trf_worst} / rise: {tr_worst}, fall: {tf_worst}'
            + f'in corner {worst_env}')
        self.logger.info(f'Worst delay was: {max(tdr_worst, tdf_worst)}')
        self.logger.info(f'Worst duty cycle error was: {duty_err_worst}')
        self.logger.info('---')

        # Loop on trf to take into account loading from unit cells that are nominally off
        # but not included in single unit-cell design script.
        for i in range(1, max_iter + 1):
            scale_error = trf_worst / trf_max

            if scale_error < 1:
                break

            core_params['r_targ'] = core_params['r_targ'] / scale_error * (
                1 - 1 / (2 * max_iter))
            self.logger.info(f'Scale error set to {scale_error}.')
            self.logger.info(
                f'New target R per segment set to {core_params["r_targ"]}')

            core_designer.set_dsn_specs(core_params)
            # TODO: Allow designer to start from previous design point and/or guess at scaled
            #       design (improve runtime)
            summary = await core_designer.async_design(**core_params)

            result_params = summary['dut_params']['params']

            dut_params['params']['unit_params'] = dict(
                seg_p=result_params['seg_p'],
                seg_n=result_params['seg_n'],
                seg_nand=result_params['seg_nand'],
                seg_nor=result_params['seg_nor'],
                w_p=result_params['w_p'],
                w_n=result_params['w_n'],
                w_p_nand=result_params['w_p_nand'],
                w_n_nand=result_params['w_n_nand'],
                w_p_nor=result_params['w_p_nor'],
                w_n_nor=result_params['w_n_nor'],
            )

            tbm_specs['sim_params']['tbit'] = 1 / freq
            # tbm_specs['sim_params']['tbit'] = 20 * core_params['r_targ'] * c_ext

            trf_worst = -float('inf')
            tdr_worst = -float('inf')
            tdf_worst = -float('inf')
            duty_err_worst = 0
            worst_env = ''
            duty_env = ''
            dut = await self.async_new_dut('output_driver', STDCellWrapper,
                                           dut_params)
            for env in all_corners['envs']:
                tbm_specs['sim_envs'] = [env]
                tbm_specs['sim_params']['vdd'] = all_corners['vddio'][env]
                tbm = cast(CombLogicTimingTB,
                           self.make_tbm(CombLogicTimingTB, tbm_specs))

                sim_results = await self.async_simulate_tbm_obj(
                    f'sim_final_{i}_{env}', dut, tbm, tb_params)
                tdr, tdf = CombLogicTimingTB.get_output_delay(
                    sim_results.data, tbm_specs, 'in', 'out', False)

                if tdr[0] > tdr_worst:
                    tdr_worst = tdr[0]
                if tdf[0] > tdf_worst:
                    tdf_worst = tdf[0]

                duty_err = tdr[0] - tdf[0]
                if np.abs(duty_err) > np.abs(duty_err_worst):
                    duty_err_worst = duty_err
                    duty_env = env

                tr, tf = CombLogicTimingTB.get_output_trf(
                    sim_results.data, tbm_specs, 'out')
                trf = max(np.max(tr), np.max(tf))

                if trf > trf_worst:
                    trf_worst = trf
                    tr_worst = tr[0]
                    tf_worst = tf[0]
                    worst_env = env

            self.logger.info('---')
            self.logger.info(f'Completed design iteration {i}.')
            self.logger.info(
                f'Worst trf was: {trf_worst} / rise: {tr_worst}, fall: {tf_worst}'
                + f'in corner {worst_env}')
            self.logger.info(
                f'Worst delay was: {max(tdr_worst, tdf_worst)} in corner {worst_env}'
            )
            self.logger.info(
                f'Worst duty cycle error was: {duty_err_worst} in corner {duty_env}'
            )
            self.logger.info('')

        return dict(dut_params=dut_params,
                    tdr=tdr_worst,
                    tdf=tdf_worst,
                    trf_worst=trf_worst,
                    duty_err=duty_err_worst)
Exemple #10
0
    async def _resize_transistors(self,
                                  dut_params: Dict[str, Any],
                                  mm_specs: Dict[str, Any],
                                  r_targ: float,
                                  r_pu: np.ndarray,
                                  r_pd: np.ndarray,
                                  seg_min: int,
                                  seg_even: bool,
                                  rel_err: float,
                                  max_iter: int = 4,
                                  is_weak: Optional[bool] = False,
                                  seg_p: int = 0,
                                  seg_n: int = 0,
                                  seg_max: int = 20) -> Dict[str, Any]:
        """Iteratively searches transistor segments and width to hit target r_targ output
        resistance. If no result is found up to seg_max, width is adjusted, and then this
        function is recursively called.

        Parameters
        ----------
        dut_params: Mapping[str, Any]
            Driver generator parameters
        mm_specs: Dict[str, Any]
            Specs for DriverPullUpDownMM
        r_targ: float
            Target output resistance
        r_pu: np.ndarray
            Measured pull-up output resistance across given corners, from previous search
        r_pd: np.ndarray
            Measured pull-down output resistance across given corners, from previous search
        seg_min: int
            Min. allowed segments
        seg_even: bool
            True to force number of segments to be even
        rel_err: float
            Output resistance error tolerance
        max_iter: int
            Maximum allowed number of iteration
        is_weak: Optional[bool]
            Deprecated: True if sizing weak driver
        seg_p: int
            If given, used as initial number of PMOS segments instead of seg_min
        seg_n: int
            If given, used as initial number of NMOS segments instead of seg_min
        seg_max: int
            Max. allowed segments

        Returns
        -------
        ans: Mapping[str, Any]
            Design summary, including generator parameters and performance summary
        """
        seg_p = seg_min if seg_p == 0 else seg_p
        seg_n = seg_min if seg_n == 0 else seg_n
        seg_p_best = seg_p
        seg_n_best = seg_n
        err_best_p = float('inf')
        err_best_n = float('inf')
        visited = set()
        cnt = 0
        r_pu_best = float('inf')
        r_pd_best = float('inf')

        for cnt in range(seg_max):
            if np.max(r_pu) > (1 + rel_err) * r_targ:
                seg_p += 1
            elif np.max(r_pu) < (1 - rel_err) * r_targ and seg_p >= 2:
                seg_p -= 1

            if np.max(r_pd) > (1 + rel_err) * r_targ:
                seg_n += 1
            elif np.max(r_pd) < (1 - rel_err) * r_targ and seg_n >= 2:
                seg_n -= 1

            new_tuple = (seg_p, seg_n)
            if new_tuple in visited:
                break
            else:
                visited.add(new_tuple)

            dut_params['seg_p'] = seg_p
            dut_params['seg_n'] = seg_n
            r_pu, r_pd = await self._get_resistance(f'resize_{cnt}',
                                                    dut_params, mm_specs)

            err_p = np.abs(np.max(r_pu) - r_targ) / r_targ
            err_n = np.abs(np.max(r_pd) - r_targ) / r_targ

            self.log(f'Iter = {cnt}, err_p={err_p:.4g}, err_n={err_n:.4g}.')
            self.log(f'Iter = {cnt}, seg_p={seg_p:.4g}, seg_n={seg_n:.4g}.')

            if err_p < err_best_p:
                err_best_p = err_p
                seg_p_best = seg_p
                r_pu_best = np.max(r_pu)

            if err_n < err_best_n:
                err_best_n = err_n
                seg_n_best = seg_n
                r_pd_best = np.max(r_pd)

            if err_p <= rel_err and err_n <= rel_err:
                break

        if cnt == max_iter - 1 or err_best_n > rel_err or err_best_p > rel_err:
            tech_globals = get_tech_global_info('aib_ams')
            dut_params['seg_p'] = seg_p_best
            dut_params['seg_n'] = seg_n_best
            # TODO: below code needs to be updated to reflect width selection
            if err_best_p > rel_err:
                dut_params['w_p'] = dut_params['w_p'] - 1
            if err_best_n > rel_err:
                dut_params['w_n'] = dut_params['w_n'] - 1
            if dut_params['w_p'] >= tech_globals['w_minp'] and \
               dut_params['w_n'] >= tech_globals['w_minn']:
                msg = f'Recursing through resize transistors with w_p: {dut_params["w_p"]} and ' \
                      f'w_n: {dut_params["w_n"]}'
                self.log(msg)
                r_pu, r_pd = await self._get_resistance(
                    f'get_res_for_new_w', dut_params, mm_specs)
                return await self._resize_transistors(dut_params, mm_specs,
                                                      r_targ, r_pu, r_pd,
                                                      seg_min, seg_even,
                                                      rel_err, max_iter,
                                                      is_weak, seg_p_best,
                                                      seg_n_best)
            else:
                msg = f'Unable to find sizing for output driver ' + \
                      f'that meets {rel_err*100:.2f}% error.'
                raise RuntimeError(msg)

        return dict(
            stack=dut_params['stack'],
            r_targ=r_targ,
            r_pu=r_pu_best,
            r_pd=r_pd_best,
            w_p=dut_params['w_p'],
            w_n=dut_params['w_n'],
            seg_p=seg_p_best,
            seg_n=seg_n_best,
            err_p=err_best_p,
            err_n=err_best_n,
        )
Exemple #11
0
    async def async_design(self,
                           c_max: float,
                           trf_in: float,
                           w_p: int,
                           w_n: int,
                           r_targ: float,
                           tile_specs: Mapping[str, Any],
                           del_err: float,
                           tile_name: str,
                           seg_even: Optional[bool] = False,
                           **kwargs: Any) -> Mapping[str, Any]:
        """This function designs the main output driver unit cell
        1) Calls DriverPullUpDownDesigner to design output pull up / pull down
        2) Design input NAND and NOR
        3) Characterize and sign off

        Parameters
        ----------
        c_max: float
            Target load capacitance
        trf_in: float
            Input rise / fall time in simulation
        w_p: int
            Initial output PMOS width
        w_n: int
            Initial output NMOS width
        r_targ: float:
            Target nominal (strong) output resistance.
        tile_name: str
            Tile name for layout.
        del_err: float
            Delay mismatch tolerance, for sizing NAND + NOR
        tile_specs: Mapping[str, Any]
            Tile specifications for layout.
        seg_even: Optional[bool]
            True to force segments to be even
        kwargs: Any
            Additional keyword arguments. Unused here

        Returns
        -------
        ans: Mapping[str, Any]
            Design summary, including performance specs and generator parameters
        """
        tinfo_table = TileInfoTable.make_tiles(self.grid, tile_specs)
        self.pinfo = tinfo_table[tile_name]

        self.out_w_p = w_p
        self.out_w_n = w_n

        self.dsn_specs['w_p'] = self.out_w_p
        self.dsn_specs['w_n'] = self.out_w_n

        driver_designer = DriverPullUpDownDesigner(self._root_dir,
                                                   self._sim_db,
                                                   self.dsn_specs)
        summary = await driver_designer.async_design(
            **driver_designer.dsn_specs)

        seg_p: int = summary['seg_p']
        seg_n: int = summary['seg_n']
        self.out_w_p = summary['w_p']
        self.out_w_n = summary['w_n']

        tbm_specs: dict = dict(sim_envs=get_tech_global_info('aib_ams')
                               ['dsn_envs']['slow_io']['env'],
                               thres_lo=0.1,
                               thres_hi=0.9,
                               tstep=None,
                               sim_params=dict(
                                   vdd=get_tech_global_info('aib_ams')
                                   ['dsn_envs']['slow_io']['vddio'],
                                   cload=c_max,
                                   tbit=20 * r_targ * c_max,
                                   trf=trf_in,
                               ),
                               rtol=1e-8,
                               atol=1e-22,
                               save_outputs=['out', 'nand_pu', 'nor_pd', 'in'])
        gate_sizes = await self._size_nand_nor(seg_p, seg_n, trf_in, tbm_specs,
                                               del_err, seg_even)
        nand_seg, nor_seg, nand_p_w_del, nor_n_w_del, w_nom = gate_sizes

        # Characterize Final design
        dut_params = self._get_unit_cell_params(self.pinfo,
                                                seg_p,
                                                seg_n,
                                                nand_seg,
                                                nor_seg,
                                                nand_p_w_del,
                                                nor_n_w_del,
                                                w_min=w_nom)
        dut = await self.async_new_dut('unit_cell', STDCellWrapper, dut_params)

        all_corners = get_tech_global_info(
            'aib_ams')['signoff_envs']['all_corners']
        tdr_worst = -float('inf')
        tdf_worst = -float('inf')
        pu_tr_worst = -float('inf')
        pu_tf_worst = -float('inf')
        pd_tr_worst = -float('inf')
        pd_tf_worst = -float('inf')
        duty_err_worst = 0
        for env in all_corners['envs']:
            tbm_specs['sim_envs'] = [env]
            tbm_specs['sim_params']['vdd'] = all_corners['vddio'][env]
            tbm = cast(CombLogicTimingTB,
                       self.make_tbm(CombLogicTimingTB, tbm_specs))
            sim_results = await self.async_simulate_tbm_obj(
                f'sim_final_{env}', dut, tbm, self._tb_params)

            tdr, tdf = CombLogicTimingTB.get_output_delay(
                sim_results.data, tbm_specs, 'in', 'out', False)
            pu_tr, pu_tf = CombLogicTimingTB.get_output_trf(
                sim_results.data, tbm_specs, 'nand_pu')
            pd_tr, pd_tf = CombLogicTimingTB.get_output_trf(
                sim_results.data, tbm_specs, 'nor_pd')

            tdr_worst = self.set_worst_spec(tdr, tdr_worst)
            tdf_worst = self.set_worst_spec(tdf, tdf_worst)
            pu_tr_worst = self.set_worst_spec(pu_tr, pu_tr_worst)
            pu_tf_worst = self.set_worst_spec(pu_tf, pu_tf_worst)
            pd_tr_worst = self.set_worst_spec(pd_tr, pd_tr_worst)
            pd_tf_worst = self.set_worst_spec(pd_tf, pd_tf_worst)

            duty_err = tdr - tdf
            if np.abs(duty_err) > np.abs(duty_err_worst):
                duty_err_worst = duty_err

        return dict(tdr=tdr_worst,
                    tdf=tdf_worst,
                    pu_tr=pu_tr_worst,
                    pu_tf=pu_tf_worst,
                    pd_tr=pd_tr_worst,
                    pd_tf=pd_tf_worst,
                    duty_err=duty_err_worst,
                    dut_params=dut_params)
Exemple #12
0
    async def _find_tgate_and_tint(self, inv_in_pseg, inv_in_nseg, pseg, nseg,
                                   inv_nseg, inv_pseg, out_inv_m, pseg_off,
                                   inv_input_cap, cload, k_ratio, pinfo,
                                   tbm_specs, is_ctrl, has_rst, dual_output,
                                   vin, vout,
                                   worst_env) -> Tuple[dict, dict, float]:

        # Setup nominal parameters and delay
        lv_params = dict(
            pinfo=pinfo,
            seg_p=pseg,
            seg_n=nseg,
            seg_inv_p=inv_pseg,
            seg_inv_n=inv_nseg,
            seg_in_inv_n=inv_in_nseg,
            seg_in_inv_p=inv_in_pseg,
            out_inv_m=out_inv_m,
            out_pseg_off=pseg_off,
        )
        tb_params = self._get_full_tb_params()
        dut_params = self._get_lvl_shift_params_dict(**lv_params,
                                                     has_rst=has_rst,
                                                     dual_output=False,
                                                     skew_out=True)
        dut = await self.async_new_dut('lvshift', STDCellWrapper, dut_params)
        all_corners = get_tech_global_info(
            'bag3_digital')['signoff_envs']['all_corners']
        tbm_specs['sim_envs'] = [worst_env]
        tbm_specs['sim_params']['vdd_in'] = all_corners[vin][worst_env]
        tbm_specs['sim_params']['vdd'] = all_corners[vout][worst_env]
        tbm = cast(CombLogicTimingTB,
                   self.make_tbm(CombLogicTimingTB, tbm_specs))
        sim_results_orig = await self.async_simulate_tbm_obj(
            f'sim_output_inv_pseg_{pseg_off}', dut, tbm, tb_params)
        tdr_nom, tdf_nom = CombLogicTimingTB.get_output_delay(
            sim_results_orig.data,
            tbm.specs,
            'in',
            'out',
            False,
            in_pwr='vdd_in',
            out_pwr='vdd')

        slope_dict = dict()
        tot_sense_dict = dict()
        tint_dict = dict()
        segs_out = cload / inv_input_cap
        tweak_vars = [
            ('seg_in_inv_p', 'in', 'inb_buf', 'fall', True, 'vdd_in', 'vdd_in',
             inv_in_pseg + inv_in_nseg,
             nseg + inv_nseg + inv_pseg + (pseg if has_rst else 0), 0),
            ('seg_n', 'inb_buf', 'midp', 'rise', True, 'vdd_in', 'vdd', nseg,
             pseg, 1 / k_ratio),
            ('seg_p', 'midp', 'midn', 'fall', True, 'vdd', 'vdd', pseg,
             2 * out_inv_m - pseg_off, 1),
            ('out_inv_m', 'midn', 'out', 'rise', True, 'vdd', 'vdd',
             2 * out_inv_m - pseg_off, segs_out, 0)
        ]
        tint_tot = 0
        for var_tuple in tweak_vars:
            var, node_in, node_out, in_edge, invert, in_sup, out_sup, seg_in, seg_load, fan_min = var_tuple
            tdr_stg_nom, tdf_stg_nom = CombLogicTimingTB.get_output_delay(
                sim_results_orig.data,
                tbm.specs,
                node_in,
                node_out,
                invert,
                in_pwr=in_sup,
                out_pwr=out_sup)

            nom_var = lv_params[var]
            lv_params[var] = nom_var + 1

            dut_params = self._get_lvl_shift_params_dict(**lv_params,
                                                         has_rst=has_rst,
                                                         dual_output=False,
                                                         skew_out=True)
            dut = await self.async_new_dut('lvshift', STDCellWrapper,
                                           dut_params)
            sim_results = await self.async_simulate_tbm_obj(
                f'sim_check_tgate_tint', dut, tbm, tb_params)
            tdr_new, tdf_new = CombLogicTimingTB.get_output_delay(
                sim_results.data,
                tbm.specs,
                'in',
                'out',
                False,
                in_pwr='vdd_in',
                out_pwr='vdd')
            tdr_stg_new, tdf_stg_new = CombLogicTimingTB.get_output_delay(
                sim_results.data,
                tbm.specs,
                node_in,
                node_out,
                invert,
                in_pwr=in_sup,
                out_pwr=out_sup)
            td_new, td_nom = (tdr_stg_new,
                              tdr_stg_nom) if in_edge == 'rise' else (
                                  tdf_stg_new, tdf_stg_nom)

            slope_dict[var] = (td_nom[0] -
                               td_new[0]) / (seg_load / seg_in - seg_load /
                                             (seg_in + 1))
            tot_sense_dict[var] = tdf_nom[0] - tdf_new[0]
            tint_dict[var] = td_nom[0] - slope_dict[var] * seg_load / seg_in
            lv_params[var] = nom_var
            tint_tot += fan_min * slope_dict[var] + tint_dict[var]

        return slope_dict, tint_dict, tint_tot
Exemple #13
0
    async def async_design(self,
                           r_targ: float,
                           c_max: float,
                           freq: float,
                           trf_in: float,
                           rel_err: float,
                           tile_specs: Mapping[str, Any],
                           tile_name: str,
                           w_p: int,
                           w_n: int,
                           res_mm_specs: Dict[str, Any],
                           is_weak: Optional[bool] = False,
                           stack_max: Optional[int] = 10,
                           seg_even: bool = True,
                           em_options: Optional[Mapping[str, Any]] = None,
                           ridx_p: int = -1,
                           ridx_n: int = 0,
                           max_iter: int = 10,
                           **kwargs: Any) -> Mapping[str, Any]:
        """Design a driver unit cell.
        will try to achieve the given maximum trf and meet EM specs at c_max.
        The main driver increases the segments and width to achieve small output resistance
        The weak driver uses stacking to achieve large output resistance.

        1) Determines minimum transistor segments for output current for given load cap and
        frequency
        2) For weak driver, determines transistor stacking to meet output resistance spec
        3) For main driver, determines transistor segments and width to meet output resistance spec

        Parameters
        ----------
        r_targ: float
            Target unit cell output resistance
        c_max: float
            Load capacitance
        freq: float
            Operating switching frequency
        trf_in: float
            Input rise / fall time
        rel_err: float
            Output resistance error tolerance
        tile_name: str
            Tile name for layout.
        tile_specs: Mapping[str, Any]
            Tile specifications for layout.
        w_p: int
            Initial output PMOS width
        w_n: int
            Initial output NMOS width
        res_mm_specs: Dict[str, Any]
            Specs for DriverPullUpDownMM
        is_weak: Optional[bool]
            True if designing weak driver
        stack_max: int
            Maximum allowed transistor stack size
        seg_even: bool
            True to force number of segments to be even
        em_options: Optional[Mapping[str, Any]]
            Additional arguments for calculating minimum segments based on EM specs
        ridx_n: int
            NMOS transistor row
        ridx_p: int
            PMOS transistor row
        max_iter: int
            Maximum allowed number of iteration
        kwargs: Any
            Additional keyword arguments. Unused here

        Returns
        -------
        ans: Mapping[str, Any]
            Design summary, including generator parameters and performance summary
        """
        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)

        if em_options is None:
            em_options = {}

        tinfo_table = TileInfoTable.make_tiles(self.grid, tile_specs)
        arr_info = tinfo_table.arr_info
        pinfo = tinfo_table[tile_name]
        mos_tech = arr_info.tech_cls

        # get transistor parameters
        vdd_max = get_tech_global_info(
            'aib_ams')['signoff_envs']['vmax']['vddio']
        iac_peak = c_max * vdd_max * freq
        iac_rms = iac_peak / math.sqrt(2)
        seg_min = mos_tech.get_segments_from_em(
            arr_info.conn_layer, 0, iac_rms, iac_peak, even=True, **
            em_options) if not is_weak else 1
        self.log(f'seg_min = {seg_min}')

        dut_params = dict(
            pinfo=pinfo,
            seg_p=seg_min,
            seg_n=seg_min,
            w_p=w_p,
            w_n=w_n,
            stack=1,
            ridx_p=ridx_p,
            ridx_n=ridx_n,
            export_pins=True,
        )

        # get number of stacks
        # Modified to set effectively skip stacking for main driver in order to improve run time
        r_pu, r_pd = await self._get_stack(dut_params, res_mm_specs, r_targ,
                                           stack_max if is_weak else None)
        self.log(f'stack = {dut_params["stack"]}')

        if is_weak:
            if gen_specs is not None and gen_cell_args is not None:
                gen_cell_specs = dict(
                    lay_class=STDCellWrapper.get_qualified_name(),
                    params=dict(
                        cls_name=PullUpDown.get_qualified_name(),
                        params=dut_params,
                    ),
                    **gen_specs,
                )
                return dict(gen_specs=gen_cell_specs, gen_args=gen_cell_args)

            return dict(
                stack=dut_params['stack'],
                r_targ=r_targ,
                r_pu=r_pu,
                r_pd=r_pd,
                seg_p=seg_min,
                seg_n=seg_min,
                w_p=w_p,
                w_n=w_n,
            )

        # get widths/number of segments
        ans = await self._resize_transistors(dut_params,
                                             res_mm_specs,
                                             r_targ,
                                             r_pu,
                                             r_pd,
                                             seg_min,
                                             seg_even,
                                             rel_err,
                                             max_iter=6)

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

        return ans
Exemple #14
0
    async def async_design(self,
                           nsync: int,
                           cload: float,
                           fmax: float,
                           fmin: float,
                           dcd_max: float,
                           rel_del_max: float,
                           pinfo: Mapping[str, any],
                           core_params: Optional[Dict[str, Any]] = None,
                           sync_params: Optional[Dict[str, Any]] = None,
                           buf_params: Optional[Dict[str, Any]] = None,
                           **kwargs: Mapping[str, Any]) -> Mapping[str, Any]:
        """
        This design method is basically a logical-effort sizing and a sign-off in the end.
        If there are sizes passed in through optional parameters they will be used and this
        function will only execute the sign-off part.

        Parameters
        ----------
        nsync: int 
            Number of synchronizer flops.
        cload: float
            Loading cap.
        fmax: float
            Max. frequency for sign-off.
        fmin: float
            Min. frequency for sign-off.
        dcd_max: float
            Max. duty cycle distortion allowed.
        rel_del_max: float
            Max. relative delay requirement between launch and measure edges at output.
        pinfo: Mapping[str, any]:
            pinfo for layout.
        core_params: Dict[str, Any]
            Optional. If provided core design part will be skipped.
        sync_params: Dict[str, Any]
            Optional. If provided synchronizer design part will be skipped.
        buf_params: Dict[str, Any]
            Optional. If provided buffer design part will be skipped.

        Returns
        -------
        summary: Mapping[str, Any]
            Design summary.
        """
        tech_globals = get_tech_global_info('aib_ams')

        core_params = self._default_core_params(
        ) if core_params is None else core_params
        sync_params = self._default_sync_params(
        ) if sync_params is None else sync_params
        buf_params = self._default_buf_params(
        ) if buf_params is None else buf_params
        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)

        if 'width' not in pinfo['row_specs'][0] and 'width' not in pinfo[
                'row_specs'][1]:
            pinfo['row_specs'][0]['width'] = 2 * tech_globals['w_minn']
            pwidth = int(
                np.round(tech_globals['inv_beta'] * 2 *
                         tech_globals['w_minn']))
            pinfo['row_specs'][1]['width'] = pwidth

        gen_params = dict(
            cls_name=self.get_dut_lay_class().get_qualified_name(),
            draw_taps=True,
            params=dict(
                pinfo=pinfo,
                core_params=core_params,
                sync_params=sync_params,
                buf_params=buf_params,
                nsync=nsync,
            ),
        )

        dut = await self.async_new_dut("dcc_helper", STDCellWrapper,
                                       gen_params)

        helper = GatherHelper()
        for env_str in tech_globals['signoff_envs']['all_corners']['envs']:
            for freq in [fmin, fmax]:
                helper.append(
                    self._sim_and_check_specs(dut, env_str, freq, cload,
                                              dcd_max, fmax, rel_del_max))

        results = await helper.gather_err()

        dcd_dict = {}
        del_rel_dict = {}
        idx = 0
        for env_str in tech_globals['signoff_envs']['all_corners']['envs']:
            for freq in ['fmin', 'fmax']:
                if freq == 'fmax':
                    dcd_dict[env_str] = results[idx][1]
                    del_rel_dict[env_str] = results[idx][2]
                idx += 1

        dcd_min = min(dcd_dict.values())
        dcd_max = max(dcd_dict.values())
        del_rel_min = min(del_rel_dict.values())
        del_rel_max = max(del_rel_dict.values())
        self.log(
            f'|DCD| ranges from {dcd_min*100:.4f}% to {dcd_max*100:.4f}%.')
        self.log(
            f'Relative delay ranges from {del_rel_min*100:.4f}% to {del_rel_max*100:.4f}%'
        )

        if gen_specs is not None and gen_cell_args is not None:
            gen_cell_specs = dict(
                lay_class=STDCellWrapper.get_qualified_name(),
                cls_name=DCCHelper.get_qualified_name(),
                params=gen_params,
                **gen_specs,
            )
            return dict(gen_specs=gen_cell_specs, gen_args=gen_cell_args)

        return gen_params
Exemple #15
0
    async def _size_nand_nor(
            self,
            seg_p: int,
            seg_n: int,
            trf: float,
            tbm_specs: dict,
            del_err: float,
            seg_even: Optional[bool] = False,
            nand_p_w_del: Optional[int] = None,
            nor_n_w_del: Optional[int] = None,
            w_nom: Optional[int] = -1) -> Tuple[int, int, int, int, int]:

        # Check for defaults and set them if needed.
        tech_globals = get_tech_global_info('aib_ams')
        if nand_p_w_del is None:
            nand_p_w_del = 0
        if nor_n_w_del is None:
            nor_n_w_del = 0

        if w_nom == -1:
            w_nom = max(tech_globals['w_nomp'], tech_globals['w_nomn'])

        # binary search: up size both nand and nor to meet the slope rate trf
        nand_seg = nor_seg = 1
        max_nand_seg = int(np.round(seg_p / tech_globals['min_fanout']))
        max_nor_seg = int(np.round(seg_n / tech_globals['min_fanout']))
        dut_params = self._get_unit_cell_params(self.pinfo,
                                                seg_p,
                                                seg_n,
                                                nand_seg,
                                                nor_seg,
                                                nand_p_w_del,
                                                nor_n_w_del,
                                                w_min=w_nom)

        tbm_specs['sim_envs'] = tech_globals['dsn_envs']['slow_io']['env']
        tbm_specs['sim_params']['vdd'] = tech_globals['dsn_envs']['slow_io'][
            'vddio']
        tbm = cast(CombLogicTimingTB,
                   self.make_tbm(CombLogicTimingTB, tbm_specs))
        nand_seg, _, tf = await self._upsize_gate_for_trf(
            dut_params, trf, nand_seg, True, tbm, seg_even, max_nand_seg)
        nor_seg, tr, _ = await self._upsize_gate_for_trf(
            dut_params, trf, nor_seg, False, tbm, seg_even, max_nor_seg)

        # Change tbm_spec to design for delay matching in tt corner
        dut_params_new = self._get_unit_cell_params(self.pinfo,
                                                    seg_p,
                                                    seg_n,
                                                    nand_seg,
                                                    nor_seg,
                                                    nand_p_w_del,
                                                    nor_n_w_del,
                                                    w_min=w_nom)
        tbm_specs['sim_envs'] = tech_globals['dsn_envs']['center']['env']
        tbm_specs['sim_params']['vdd'] = tech_globals['dsn_envs']['center'][
            'vddio']
        tbm = cast(CombLogicTimingTB,
                   self.make_tbm(CombLogicTimingTB, tbm_specs))
        dut = await self.async_new_dut('nand_nor_check_delay', STDCellWrapper,
                                       dut_params_new)
        sim_results = await self.async_simulate_tbm_obj(
            'check_delay', dut, tbm, self._tb_params)

        nand_tdf, nand_tdr = CombLogicTimingTB.get_output_delay(
            sim_results.data,
            tbm.specs,
            'in',
            'nand_pu',
            out_invert=True,
            out_pwr='vdd')
        nor_tdf, nor_tdr = CombLogicTimingTB.get_output_delay(sim_results.data,
                                                              tbm.specs,
                                                              'in',
                                                              'nor_pd',
                                                              out_invert=True,
                                                              out_pwr='vdd')

        err_cur = np.abs(nand_tdf - nor_tdr)
        if np.max(err_cur) > del_err:
            # up_size the slower side to make it faster
            if np.max(nor_tdr) < np.max(nand_tdf):
                nand_seg, nand_tdr, nand_tdf = await self._upsize_gate_for_delay(
                    dut_params, np.max(nor_tdr), nand_seg, True, tbm, seg_even,
                    max_nand_seg)
            else:
                nor_seg, nor_tdr, nor_tdf = await self._upsize_gate_for_delay(
                    dut_params, np.max(nand_tdf), nor_seg, False, tbm,
                    seg_even, max_nor_seg)

        # check if t_rise_nand < t_rise_nor and t_fall_nor < t_fall_nand
        rerun = False
        if not np.all(nand_tdr < nor_tdr):
            rerun = nand_p_w_del + 1 + w_nom <= tech_globals['w_maxp']
            new_nand_p_w_del = nand_p_w_del + 1 if rerun else nand_p_w_del
        else:
            new_nand_p_w_del = nand_p_w_del

        if not np.all(nor_tdf < nand_tdf):
            rerun = nor_n_w_del + 1 + w_nom <= tech_globals['w_maxn']
            new_nor_n_w_del = nor_n_w_del + 1 if rerun else nor_n_w_del
        else:
            new_nor_n_w_del = nor_n_w_del

        if rerun:
            await self._size_nand_nor(seg_p, seg_n, trf, tbm_specs, del_err,
                                      seg_even, new_nand_p_w_del,
                                      new_nor_n_w_del, w_nom)

        await self.run_nand_nor_signoff(dut, del_err, tbm_specs, tech_globals)

        return nand_seg, nor_seg, nand_p_w_del, nor_n_w_del, w_nom
Exemple #16
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
Exemple #17
0
    def _build_meas_params(cls, sim_env: str, freq: float,
                           cload: float) -> Dict[str, Any]:
        """
        Creates parameter dictionary for ClockDelayMM measurement manager class. ClockDelayMM 
        uses DigitalTranTB related test-bench manager parameters.
        
        Parameters
        ----------
        sim_env: str
            Corner-temperature environment.
        freq: float
            Frequency of clk in simulation.
        cload: float
            Loading cap.
            
        Returns
        -------
        meas_params: Dict[str, Any]
            Measurement dictionary params.
        """
        trf_nom = get_tech_global_info('aib_ams')['trf_nom']
        sim_env_info = get_tech_global_info(
            'aib_ams')['signoff_envs']['all_corners']
        meas_params = dict(
            tbm_specs=dict(
                sim_envs=[sim_env],
                sim_params=dict(  # simulation parameters
                    t_rst=1.1 / freq,
                    t_rst_rf=trf_nom,
                    t_bit=1 / freq,
                ),
                thres_lo=0.1,
                thres_hi=0.9,
                rtol=1.0e-8,
                atol=1.0e-22,
                tran_options=dict(
                    maxstep=1.0e-12,
                    errpreset='conservative',
                ),
                swp_info=[  # list of parameters to sweep
                    ['t_rf', {
                        'type': 'LIST',
                        'values': [trf_nom]
                    }], ['c_load', {
                        'type': 'LIST',
                        'values': [cload]
                    }]
                ],
                pwr_domain=dict(  # pin's low/high pwr domains (defined later)
                    clk_dcd=('VSS', 'VDD'),
                    dcc_byp=('VSS', 'VDD'),
                    launch=('VSS', 'VDD'),
                    measure=('VSS', 'VDD'),
                    rstb=('VSS', 'VDD'),
                    ckout=('VSS', 'VDD')),
                sup_values=dict(  # pwr domain definition with proper values
                    VDD=sim_env_info['vdd'][sim_env],
                    VSS=0.0,
                ),
                pin_values=
                dict(  # low(0) or high(1) value for pins with constant voltage
                    clk_dcd=0,
                    dcc_byp=0,
                    launch=0,
                    measure=0,
                ),
                reset_list=[['rstb', False]],  # reset rstb (active low)
            ),
            fake=False,  # make this true to get fake data (i.e. for debugging)
        )

        return meas_params
Exemple #18
0
    async def signoff_dut(
        self,
        dut,
        cload,
        vin,
        vout,
        dmax,
        trf_in,
        is_ctrl,
        has_rst,
        exception_on_dmax: bool = True
    ) -> Tuple[float, float, str, float, str]:

        tech_info = get_tech_global_info('bag3_digital')
        all_corners = tech_info['signoff_envs']['all_corners']

        # Run level shifter extreme corner signoff
        envs = tech_info['signoff_envs']['lvl_func']['env']
        vdd_out = tech_info['signoff_envs']['lvl_func']['vddo']
        vdd_in = tech_info['signoff_envs']['lvl_func']['vddi']

        tbm_specs = self._get_tbm_params(envs, vdd_in, vdd_out, trf_in, cload,
                                         10 * dmax)
        tbm_specs['save_outputs'] = [
            'in', 'inbar', 'inb_buf', 'in_buf', 'midn', 'midp', 'out', 'outb'
        ]
        tbm = cast(CombLogicTimingTB,
                   self.make_tbm(CombLogicTimingTB, tbm_specs))

        # sign off signal path
        tb_params = self._get_full_tb_params()
        sim_results = await self.async_simulate_tbm_obj(
            f'signoff_lvlshift_extreme', dut, tbm, tb_params)
        tdr, tdf = CombLogicTimingTB.get_output_delay(sim_results.data,
                                                      tbm_specs,
                                                      'in',
                                                      'out',
                                                      False,
                                                      in_pwr='vdd_in',
                                                      out_pwr='vdd')

        td = max(tdr, tdf)
        if td < float('inf'):
            self.log(
                'Level shifter signal path passed extreme corner signoff.')
        else:
            plt.plot(sim_results.data['time'].flatten(),
                     sim_results.data['in'].flatten(), 'b')
            plt.plot(sim_results.data['time'].flatten(),
                     sim_results.data['out'].flatten(), 'g')
            plt.show(block=False)
            raise ValueError(
                'Level shifter design failed extreme corner signoff.')

        # sign off reset
        if has_rst:
            tbm_specs['stimuli_pwr'] = 'vdd'
            tbm = cast(CombLogicTimingTB,
                       self.make_tbm(CombLogicTimingTB, tbm_specs))
            rst_tb_params = self._get_rst_tb_params()
            sim_results = await self.async_simulate_tbm_obj(
                f'signoff_lvlshift_rst_extreme', dut, tbm, rst_tb_params)
            tdr, tdf = CombLogicTimingTB.get_output_delay(sim_results.data,
                                                          tbm_specs,
                                                          'in',
                                                          'out',
                                                          False,
                                                          in_pwr='vdd_in',
                                                          out_pwr='vdd')
            self.log(f"Reset Delay Overall: tdr: {tdr}, tdf: {tdf} ")
            td = max(tdr, tdf)
            if td < float('inf'):
                self.log(
                    'Level shifter reset path passed extreme corner signoff.')
            else:
                plt.plot(sim_results.data['time'].flatten(),
                         sim_results.data['in'].flatten(), 'b')
                plt.plot(sim_results.data['time'].flatten(),
                         sim_results.data['out'].flatten(), 'g')
                plt.show(block=False)
                raise ValueError(
                    'Level shifter design failed reset extreme corner signoff.'
                )

        envs = all_corners['envs']
        worst_trst = -float('inf')
        worst_td = -float('inf')
        worst_tdf = -float('inf')
        worst_tdr = -float('inf')
        worst_var = 0
        worst_env = ''
        worst_var_env = ''

        for env in envs:
            vdd_in = all_corners[vin][env]
            vdd_out = all_corners[vout][env]
            tbm_specs = self._get_tbm_params([env], vdd_in, vdd_out, trf_in,
                                             cload, 10 * dmax)
            tbm_specs['stimuli_pwr'] = 'vdd_in'
            tbm_specs['save_outputs'] = [
                'in', 'inb_buf', 'in_buf', 'midn', 'midp', 'out'
            ]
            tbm = cast(CombLogicTimingTB,
                       self.make_tbm(CombLogicTimingTB, tbm_specs))

            # sign off signal path
            tb_params = self._get_full_tb_params()
            sim_results = await self.async_simulate_tbm_obj(
                f'signoff_lvlshift_{env}', dut, tbm, tb_params)
            tdr, tdf = CombLogicTimingTB.get_output_delay(sim_results.data,
                                                          tbm_specs,
                                                          'in',
                                                          'out',
                                                          False,
                                                          in_pwr='vdd_in',
                                                          out_pwr='vdd')
            self.log(f"Delay Overall: tdr: {tdr}, tdf: {tdf} ")

            td = max(tdr, tdf)
            if td > worst_td:
                worst_td = td
                worst_tdf = tdf
                worst_tdr = tdr
                worst_env = env

            if not is_ctrl:
                delay_var = (tdr - tdf)
                if np.abs(delay_var) > np.abs(worst_var):
                    worst_var = delay_var
                    worst_var_env = env
            '''
            # Debug
            # -----
            td_iinv_r, td_iinv_f = CombLogicTimingTB.get_output_delay(sim_results.data, tbm.specs, 'in',
                                                          'inb_buf', True, in_pwr='vdd_in',
                                                          out_pwr='vdd_in')
            td_minv_r, td_minv_f = CombLogicTimingTB.get_output_delay(sim_results.data, tbm.specs, 'inb_buf',
                                                          'in_buf', True, in_pwr='vdd_in',
                                                          out_pwr='vdd_in')
            td_pdn_r, td_pdn_f = CombLogicTimingTB.get_output_delay(sim_results.data, tbm.specs, 'in_buf',
                                                          'midn', True, in_pwr='vdd_in',
                                                          out_pwr='vdd')
            td_oinv_r, td_oinv_f = CombLogicTimingTB.get_output_delay(sim_results.data, tbm.specs, 'midn',
                                                          'out', True, in_pwr='vdd_in',
                                                          out_pwr='vdd')
            td_pdp_r, td_pdp_f = CombLogicTimingTB.get_output_delay(sim_results.data, tbm.specs, 'inb_buf',
                                                          'midp', True, in_pwr='vdd_in',
                                                          out_pwr='vdd')
            td_pun_r, td_pun_f = CombLogicTimingTB.get_output_delay(sim_results.data, tbm.specs, 'midp',
                                                          'midn', True, in_pwr='vdd',
                                                          out_pwr='vdd')

            if env == 'ss_125':
                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'{env}')
                plt.show(block=False)

                print(f'Path rise out: {td_iinv_r} + {td_minv_f} + {td_pdn_r} + {td_oinv_f}')
                print(f'Path fall out: {td_iinv_f} + {td_pdp_r} + {td_pun_f} + {td_oinv_r}')
                breakpoint()
            # ----
            '''

            # sign off reset
            if has_rst:
                tbm_specs['stimuli_pwr'] = 'vdd'
                tbm = cast(CombLogicTimingTB,
                           self.make_tbm(CombLogicTimingTB, tbm_specs))
                rst_tb_params = self._get_rst_tb_params()
                sim_results = await self.async_simulate_tbm_obj(
                    f'signoff_lvlshift_rst_{env}', dut, tbm, rst_tb_params)
                tdr, tdf = CombLogicTimingTB.get_output_delay(sim_results.data,
                                                              tbm_specs,
                                                              'in',
                                                              'out',
                                                              False,
                                                              in_pwr='vdd_in',
                                                              out_pwr='vdd')
                self.log(f"Reset Delay Overall: tdr: {tdr}, tdf: {tdf} ")
                td = max(tdr, tdf)
                if td > worst_trst:
                    worst_trst = td
                    worst_trst_env = env

        td_target = 20 * trf_in if is_ctrl else dmax
        self.log(
            f'td_target = {td_target}, worst_tdr = {worst_tdr}, worst_tdf = {worst_tdf}, '
            f'worst_env = {worst_env}')
        #breakpoint()

        if worst_tdr > td_target or worst_tdf > td_target:
            msg = 'Level shifter delay did not meet target.'
            if exception_on_dmax:
                raise RuntimeError(msg)
            else:
                self.log(msg)
        if has_rst:
            self.log(
                f'worst_trst = {worst_trst}, worst_trst_env = {worst_trst_env}'
            )

        if worst_tdr > 20 * trf_in or worst_tdf > 20 * trf_in:
            raise RuntimeError(
                "Level shifter reset delay exceeded simulation period.")
        return worst_tdr, worst_tdf, worst_env, worst_var, worst_var_env
Exemple #19
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
Exemple #20
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
Exemple #21
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
Exemple #22
0
    async def async_design(self,
                           cload: float,
                           dmax: float,
                           trf_in: float,
                           tile_specs: Mapping[str, Any],
                           k_ratio: float,
                           tile_name: str,
                           inv_input_cap: float,
                           inv_input_cap_per_fin: float,
                           fanout: float,
                           vin: str,
                           vout: str,
                           w_p: int = 0,
                           w_n: int = 0,
                           ridx_p: int = -1,
                           ridx_n: int = 0,
                           has_rst: bool = False,
                           is_ctrl: bool = False,
                           dual_output: bool = False,
                           exception_on_dmax: bool = True,
                           del_scale: float = 1,
                           **kwargs: Any) -> Mapping[str, Any]:
        """ Design a Level Shifter
        This will try to design a level shifter to meet a maximum nominal delay, given the load cap
        """
        tech_info = get_tech_global_info('bag3_digital')
        w_p = tech_info['w_maxp'] if w_p == 0 else w_p
        w_n = tech_info['w_maxn'] if w_n == 0 else w_n
        if not 'lch' in tile_specs['arr_info']:
            tile_specs['arr_info']['lch'] = tech_info['lch_min']
        tile_specs['place_info'][tile_name]['row_specs'][0]['width'] = w_n
        tile_specs['place_info'][tile_name]['row_specs'][1]['width'] = w_p

        tinfo_table = TileInfoTable.make_tiles(self.grid, tile_specs)
        pinfo = tinfo_table[tile_name]

        # Design the output inverter, and the level shift core
        design_sim_env, vdd_in, vdd_out = self._build_env_vars(
            'center', vin, vout)
        tbm_specs = self._get_tbm_params(design_sim_env, vdd_in, vdd_out,
                                         trf_in, cload, 10 * dmax)
        tbm_specs['save_outputs'] = [
            'in', 'inbar', 'out', 'outb', 'inb_buf', 'in_buf', 'midn', 'midp'
        ]
        out_inv_m, pseg, nseg = self._design_lvl_shift_core_size(
            cload, k_ratio, inv_input_cap, fanout, is_ctrl)

        # Design the inverter creating the inverted input to the leveler
        inv_pseg, inv_nseg = await self._design_lvl_shift_internal_inv(
            pseg, nseg, out_inv_m, fanout, pinfo, tbm_specs, is_ctrl, has_rst,
            dual_output, vin, vout)

        # Design input inverter
        inv_in_nseg, inv_in_pseg = self._size_input_inv_for_fanout(
            inv_pseg, inv_nseg, pseg, nseg, fanout, has_rst)

        # Adjust the output inverter beta ratio to further reduce duty cycle distortion
        if not is_ctrl:
            pseg_off = await self._design_output_inverter(
                inv_in_pseg, inv_in_nseg, pseg, nseg, inv_nseg, inv_pseg,
                out_inv_m, fanout, pinfo, tbm_specs, has_rst, vin, vout)
        else:
            pseg_off, worst_env = 0, ''

        # Final Simulation
        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,
                                                     is_ctrl,
                                                     skew_out=not is_ctrl,
                                                     out_pseg_off=pseg_off)

        dut = await self.async_new_dut('lvshift', STDCellWrapper, dut_params)
        tdr, tdf, worst_env, worst_var, worst_var_env = await self.signoff_dut(
            dut, cload, vin, vout, dmax, trf_in, is_ctrl, has_rst,
            exception_on_dmax)

        if not is_ctrl and max(tdr, tdf) > dmax:
            # Find intrinsic delay based on stage-by-stage characterization
            tgate_dict, tint_dict, tint_tot = await self._find_tgate_and_tint(
                inv_in_pseg, inv_in_nseg, pseg, nseg, inv_nseg, inv_pseg,
                out_inv_m, pseg_off, inv_input_cap, cload, k_ratio, pinfo,
                tbm_specs, is_ctrl, has_rst, dual_output, vin, vout, worst_env)
        else:
            tint_tot = 0

        return dict(dut_params=dut_params,
                    tdr=tdr,
                    tdf=tdf,
                    tint=tint_tot,
                    worst_var=worst_var)
Exemple #23
0
 def __init__(self, *args: Any, **kwargs: Any) -> None:
     DesignerBase.__init__(self, *args, **kwargs)
     self.dsn_tech_info = get_tech_global_info('aib_ams')
     self._txanlg_dsnr = None
     self._rxanlg_dsnr = None
Exemple #24
0
    def _get_lvl_shift_params_dict(pinfo: Any,
                                   seg_p: int,
                                   seg_n: int,
                                   seg_inv_p: int,
                                   seg_inv_n: int,
                                   seg_in_inv_p: int,
                                   seg_in_inv_n: int,
                                   out_inv_m: int,
                                   has_rst: bool,
                                   dual_output: bool,
                                   is_ctrl: bool = False,
                                   skew_out: bool = False,
                                   out_pseg_off: int = 0) -> Dict[str, Any]:
        """
        Creates a dictionary of parameters for the layout class LevelShifter
        seg_n : nmos Pull down nseg
        seg_p : pmos Pull up nseg
        seg_inv : Inb_buf to In_buf inverter segments
        seg_in_inv : In to Inb_buf inverter segments
        pinfo : pinfo
        # TODO: UPDATE THIS DOCUMENTATION
        Note: This will let the width be passed through the pinfo, currently no rst
        """
        tech_info = get_tech_global_info('bag3_digital')
        wn = tech_info['w_minn'] if is_ctrl else 2 * tech_info['w_minn']
        wp = tech_info['w_minp'] if is_ctrl else 2 * tech_info['w_minp']

        if has_rst:
            seg_dict = dict(pd=seg_n,
                            pu=seg_p,
                            rst=int(np.ceil(seg_n / 2)),
                            prst=seg_p)
            w_dict = dict(pd=wn, pu=wp, rst=wn)
        else:
            seg_dict = dict(pd=seg_n, pu=seg_p)
            w_dict = dict(pd=wn, pu=wp)

        lv_params = dict(cls_name=LevelShifter.get_qualified_name(),
                         draw_taps=True,
                         pwr_gnd_list=[('VDD_in', 'VSS'), ('VDD', 'VSS')],
                         params=dict(
                             pinfo=pinfo,
                             lv_params=dict(
                                 seg_dict=seg_dict,
                                 w_dict=w_dict,
                                 has_rst=has_rst,
                                 in_upper=has_rst,
                                 dual_output=dual_output,
                             ),
                             in_buf_params=dict(
                                 segp_list=[seg_in_inv_p, seg_inv_p],
                                 segn_list=[seg_in_inv_n, seg_inv_n],
                                 w_p=wp,
                                 w_n=wn),
                             export_pins=True,
                         ))

        # Note that setting stack_p = 2 actually changes the topology of the level shifter to include PMOS devices
        # tied to the input and in series with the cross-coupled PMOS pull-ups.
        if has_rst:
            lv_params['params']['lv_params']['stack_p'] = 2

        if skew_out:
            lv_params['params']['lv_params']['buf_segn_list'] = [out_inv_m]
            lv_params['params']['lv_params']['buf_segp_list'] = [
                out_inv_m + out_pseg_off
            ]
        else:
            lv_params['params']['lv_params']['buf_seg_list'] = [out_inv_m]

        return lv_params