Ejemplo n.º 1
0
    def evaluate(self, design_list):
        # type: (List[Design]) -> List

        subckts_yaml_files = dict(
            zip(self.subckts_template.keys(),
                [[]] * len(self.subckts_template)))
        top_level_yaml_files = []
        for dsn_num, design in enumerate(design_list):
            top_specs = deepcopy(self.ver_specs)
            layout_update = top_specs['layout_params']
            measurement_update = top_specs['measurements'][0]

            params_dict = dict(zip(self.params_vec.keys(), design))
            for key, value_idx in params_dict.items():
                params_dict[key] = self.params_vec[key][value_idx]
            self.impose_constraints(params_dict)

            for key, value in params_dict.items():
                next_key = self.decend(key)
                if next_key in self.param_choices_layout.keys():
                    self.update_with_unmerged_key(layout_update, next_key,
                                                  value)
                elif next_key in self.param_choices_measurement.keys():
                    self.update_with_unmerged_key(measurement_update, next_key,
                                                  value)

            # for each subckt we partition the updated layout into individual files
            subckts_template = deepcopy(self.subckts_template)
            for subckt_key, subckt in subckts_template.items():
                subckt['layout_params'].update(**layout_update[subckt_key])
                fname = os.path.join(self.subckts_yaml_dirs[subckt_key],
                                     'params_{}.yaml'.format(str(design.id)))
                with open(fname, 'w') as f:
                    yaml.dump(subckt, f)
                subckts_yaml_files[subckt_key].append(fname)

            # for top level we create the individual yaml files
            fname = os.path.join(self.top_level_dir,
                                 'params_top_' + str(design.id) + '.yaml')
            with open_file(fname, 'w') as f:
                yaml.dump(top_specs, f)
            top_level_yaml_files.append(fname)

        top_specs = deepcopy(self.ver_specs)
        for key, value in subckts_yaml_files.items():
            subckts_template = deepcopy(self.subckts_template)
            subckts_template[key]['sweep_params']['swp_spec_file'] = value
            subckts_template[key]['root_dir'] = os.path.join(
                top_specs['root_dir'], key)

            with open_file(self.subckts_main_file[key], 'w') as f:
                yaml.dump(subckts_template[key], f)

        top_specs['sweep_params']['swp_spec_file'] = top_level_yaml_files
        with open_file(self.top_level_main_file, 'w') as f:
            yaml.dump(top_specs, f)
        results = self.generate_and_sim()
        return self.process_results(results)
    async def verify_design(self,
                            lib_name: str,
                            dsn_name: str,
                            load_from_file: bool = False) -> None:
        """Run all measurements on the given design.

        Parameters
        ----------
        lib_name : str
            library name.
        dsn_name : str
            design cell name.
        load_from_file : bool
            If True, then load existing simulation data instead of running actual simulation.
        """
        meas_list = self.specs['measurements']
        summary_fname = self.specs['summary_fname']
        view_name = self.specs['view_name']
        env_list = self.specs['env_list']
        wrapper_list = self.specs['dut_wrappers']

        wrapper_lookup = {'': dsn_name}
        for wrapper_config in wrapper_list:
            wrapper_type = wrapper_config['name']
            wrapper_lookup[wrapper_type] = self.get_wrapper_name(
                dsn_name, wrapper_type)

        result_summary = {}
        dsn_data_dir = os.path.join(self._root_dir, dsn_name)
        for meas_specs in meas_list:
            meas_type = meas_specs['meas_type']
            meas_package = meas_specs['meas_package']
            meas_cls_name = meas_specs['meas_class']
            out_fname = meas_specs['out_fname']
            meas_name = self.get_measurement_name(dsn_name, meas_type)
            data_dir = self.get_measurement_directory(dsn_name, meas_type)

            meas_module = importlib.import_module(meas_package)
            meas_cls = getattr(meas_module, meas_cls_name)

            meas_manager = meas_cls(data_dir, meas_name, lib_name, meas_specs,
                                    wrapper_lookup, [(dsn_name, view_name)],
                                    env_list)
            print('Performing measurement %s on %s' % (meas_name, dsn_name))
            meas_res = await meas_manager.async_measure_performance(
                self.prj, load_from_file=load_from_file)
            print('Measurement %s finished on %s' % (meas_name, dsn_name))

            with open_file(os.path.join(data_dir, out_fname), 'w') as f:
                yaml.dump(meas_res, f)
            result_summary[meas_type] = meas_res

        with open_file(os.path.join(dsn_data_dir, summary_fname), 'w') as f:
            yaml.dump(result_summary, f)
Ejemplo n.º 3
0
def design(amp_dsn_specs, amp_char_specs_fname, amp_char_specs_out_fname):
    nch_config = amp_dsn_specs['nch_config']
    pch_config = amp_dsn_specs['pch_config']

    print('create transistor database')
    nch_db = MOSDBDiscrete([nch_config])
    pch_db = MOSDBDiscrete([pch_config])

    nch_db.set_dsn_params(**amp_dsn_specs['nch'])
    pch_db.set_dsn_params(**amp_dsn_specs['pch'])

    result = design_amp(amp_dsn_specs, nch_db, pch_db)
    if result is None:
        raise ValueError('No solution.')

    pprint.pprint(result)

    # update characterization spec file
    amp_char_specs = read_yaml(amp_char_specs_fname)
    # update bias
    var_dict = amp_char_specs['measurements'][0]['testbenches']['ac'][
        'sim_vars']
    for key in ('vtail', 'vindc', 'voutdc'):
        var_dict[key] = result[key]
    for key in ('vdd', 'cload'):
        var_dict[key] = amp_dsn_specs[key]
    # update segments
    seg_dict = amp_char_specs['layout_params']['seg_dict']
    for key in ('in', 'load', 'tail'):
        seg_dict[key] = result['seg_' + key]

    with open_file(amp_char_specs_out_fname, 'w') as f:
        yaml.dump(amp_char_specs, f)

    return result
Ejemplo n.º 4
0
    def __init__(self, prj, spec_file):
        # type: (Optional[BagProject], str) -> None
        self.prj = prj
        self._specs = None

        if os.path.isfile(spec_file):
            self._specs = read_yaml(spec_file)
            root_dir = os.path.abspath(self._specs['root_dir'])
            save_spec_file = os.path.join(root_dir, 'specs.yaml')
        elif os.path.isdir(spec_file):
            root_dir = os.path.abspath(spec_file)
            save_spec_file = spec_file = os.path.join(root_dir, 'specs.yaml')
            self._specs = read_yaml(spec_file)
        else:
            raise ValueError(
                '%s is neither data directory or specification file.' %
                spec_file)

        self._swp_var_list = tuple(sorted(self._specs['sweep_params'].keys()))

        # save root_dir as absolute path, in this way everything will still work
        # if the user start python from a different directory.
        self._specs['root_dir'] = root_dir
        os.makedirs(root_dir, exist_ok=True)
        with open_file(save_spec_file, 'w') as f:
            yaml.dump(self._specs, f)
Ejemplo n.º 5
0
    async def async_measure_performance(self, prj, load_from_file=False):
        # type: (BagProject, bool) -> Dict[str, Any]
        """A coroutine that performs measurement.

        The measurement is done like a FSM.  On each iteration, depending on the current
        state, it creates a new testbench (or reuse an existing one) and simulate it.
        It then post-process the simulation data to determine the next FSM state, or
        if the measurement is done.

        Parameters
        ----------
        prj : BagProject
            the BagProject instance.
        load_from_file : bool
            If True, then load existing simulation data instead of running actual simulation.

        Returns
        -------
        output : Dict[str, Any]
            the last dictionary returned by process_output().
        """
        cur_state = self.get_initial_state()
        prev_output = None
        done = False

        while not done:
            # create and setup testbench
            tb_name, tb_type, tb_specs, tb_sch_params = self.get_testbench_info(cur_state,
                                                                                prev_output)

            tb_package = tb_specs['tb_package']
            tb_cls_name = tb_specs['tb_class']
            tb_module = importlib.import_module(tb_package)
            tb_cls = getattr(tb_module, tb_cls_name)
            raw_data_fname = os.path.join(self.data_dir, '%s.hdf5' % cur_state)

            tb_manager = tb_cls(raw_data_fname, tb_name, self.impl_lib, tb_specs,
                                self.sim_view_list, self.env_list)

            if load_from_file:
                print('Measurement %s in state %s, '
                      'load sim data from file.' % (self.meas_name, cur_state))
                if os.path.isfile(raw_data_fname):
                    cur_results = load_sim_file(raw_data_fname)
                else:
                    print('Cannot find data file, simulating...')
                    cur_results = await tb_manager.setup_and_simulate(prj, tb_sch_params)
            else:
                cur_results = await tb_manager.setup_and_simulate(prj, tb_sch_params)

            # process and save simulation data
            print('Measurement %s in state %s, '
                  'processing data from %s' % (self.meas_name, cur_state, tb_name))
            done, next_state, prev_output = self.process_output(cur_state, cur_results, tb_manager)
            with open_file(os.path.join(self.data_dir, '%s.yaml' % cur_state), 'w') as f:
                yaml.dump(prev_output, f)

            cur_state = next_state

        return prev_output
Ejemplo n.º 6
0
    def evaluate(self, design_list):
        # type: (List[Design]) -> List
        swp_spec_file_list = []
        sweep_params_update = deepcopy(self.ver_specs['sweep_params'])
        # del template['sweep_params']['swp_spec_file']
        for dsn_num, design in enumerate(design_list):
            # 1. translate each list to a dict with layout_params and measurement_params indication
            # 2. write those dictionaries in the corresponding param.yaml and update self.ver_specs
            specs = deepcopy(self.ver_specs)
            layout_update = specs['layout_params']
            measurement_update = specs['measurements'][0]
            params_dict = dict(zip(self.params_vec.keys(), design))
            # imposing the constraint of layout generator
            self.impose_constraints(params_dict)

            # TODO: Still cannot handle multiple measurement manager units
            for key, value_idx in params_dict.items():
                next_key = self.decend(key)
                if next_key in self.param_choices_layout.keys():
                    self.update_with_unmerged_key(layout_update, next_key, self.params_vec[key][value_idx])
                elif next_key in self.param_choices_measurement.keys():
                    self.update_with_unmerged_key(measurement_update, next_key, self.params_vec[key][value_idx])

            specs['sweep_params']['swp_spec_file'] = ['params_'+str(design.id)]

            swp_spec_file_list.append('params_'+str(design.id))
            fname = os.path.join(self.swp_spec_dir, 'params_'+str(design.id)+'.yaml')
            with open_file(fname, 'w') as f:
                yaml.dump(specs, f)

        sweep_params_update['swp_spec_file'] = swp_spec_file_list
        self.ver_specs['sweep_params'].update(sweep_params_update)
        results = self.generate_and_sim()
        return self.process_results(results)
Ejemplo n.º 7
0
    def _setup_pwl_input(cls, values, tper, tr, tran_fname):
        # type: (List[float], float) -> None

        tvec, yvec = dig_to_pwl(values, tper, tr, td=0.0)

        tran_fname = os.path.abspath(tran_fname)
        stimuli_dir = os.path.dirname(tran_fname)
        os.makedirs(stimuli_dir, exist_ok=True)
        with open_file(tran_fname, 'w') as f:
            for t, y in zip(tvec, yvec):
                f.write('%.8f %.8f\n' % (t, y))
Ejemplo n.º 8
0
    def generate_and_sim(self):
        """
        phase 1 of evaluation is generation of layout, schematic, LVS and RCX
        If any of LVS or RCX fail results_ph1 will contain Exceptions for the corresponding instance
        We proceed to phase 2 only if phase 1 was successful.
        phase 2 is running the simulation with post extracted netlist view
        Then we aggregate the results of phase 1 and phase 2 in a single list, in the same order
        that designs were ordered, if phase 1 was failed the corresponding entry will contain
        a Phase1Error exception
        """
        results = []
        with open_file(self.top_level_main_file, 'w') as f:
            yaml.dump(self.ver_specs, f)

        sim = DeepCKTDesignManager(self.bprj, self.top_level_main_file)
        if self.temp_db is None:
            self.temp_db = sim.make_tdb()
        sim.set_tdb(self.temp_db)
        results_ph1 = sim.characterize_designs(generate=True,
                                               measure=False,
                                               load_from_file=False)
        # hacky: do parallel measurements, you should not sweep anything other than 'swp_spec_file' in sweep_params
        # the new yaml files themselves should not include any sweep_param
        start = time.time()
        impl_lib = self.ver_specs['impl_lib']
        coro_list = []
        file_list = self.ver_specs['sweep_params']['swp_spec_file']
        for ph1_iter_index, combo_list in enumerate(
                sim.get_combinations_iter()):
            dsn_name = sim.get_design_name(combo_list)
            specs_fname = os.path.join(self.gen_yamls_dir,
                                       file_list[ph1_iter_index] + '.yaml')
            if isinstance(results_ph1[ph1_iter_index], Exception):
                continue
            coro_list.append(
                self.async_characterization(impl_lib, dsn_name, specs_fname))

        results_ph2 = batch_async_task(coro_list)
        print("sim time: {}".format(time.time() - start))
        # this part returns the correct order of results if some of the instances failed phase1 of evaluation
        ph2_iter_index = 0
        for ph1_iter_index, combo_list in enumerate(
                sim.get_combinations_iter()):
            if isinstance(results_ph1[ph1_iter_index], Exception):
                results.append(Phase1Error)
            else:
                results.append(results_ph2[ph2_iter_index])
                ph2_iter_index += 1
        # pprint.pprint(results)

        return results
Ejemplo n.º 9
0
    def process_ibias_data(self, write=True):
        # type: () -> None
        tb_type = 'tb_ibias'
        tb_specs = self.specs[tb_type]
        dsn_name_base = self.specs['dsn_name_base']
        root_dir = self.specs['root_dir']
        vgs_file = self.specs['vgs_file']
        layout_params = self.specs['layout_params']

        fg = layout_params['fg']
        ibias_min_fg = tb_specs['ibias_min_fg']
        ibias_max_fg = tb_specs['ibias_max_fg']
        vgs_res = tb_specs['vgs_resolution']

        ans = {}
        for val_list in self.get_combinations_iter():
            # invert PMOS ibias sign
            is_nmos = self.is_nmos(val_list)
            ibias_sgn = 1.0 if is_nmos else -1.0
            results = self.get_sim_results(tb_type, val_list)

            # assume first sweep parameter is corner, second sweep parameter is vgs
            corner_idx = results['sweep_params']['ibias'].index('corner')
            vgs = results['vgs']
            ibias = results['ibias'] * ibias_sgn  # type: np.ndarray

            wv_max = Waveform(vgs, np.amax(ibias, corner_idx), 1e-6, order=2)
            wv_min = Waveform(vgs, np.amin(ibias, corner_idx), 1e-6, order=2)
            vgs1 = wv_max.get_crossing(ibias_min_fg * fg)
            if vgs1 is None:
                vgs1 = vgs[0] if is_nmos else vgs[-1]
            vgs2 = wv_min.get_crossing(ibias_max_fg * fg)
            if vgs2 is None:
                vgs2 = vgs[-1] if is_nmos else vgs[0]

            if is_nmos:
                vgs_min, vgs_max = vgs1, vgs2
            else:
                vgs_min, vgs_max = vgs2, vgs1

            vgs_min = math.floor(vgs_min / vgs_res) * vgs_res
            vgs_max = math.ceil(vgs_max / vgs_res) * vgs_res

            dsn_name = self.get_instance_name(dsn_name_base, val_list)
            print('%s: vgs = [%.4g, %.4g]' % (dsn_name, vgs_min, vgs_max))
            ans[dsn_name] = [vgs_min, vgs_max]

        if write:
            vgs_file = os.path.join(root_dir, vgs_file)
            with open_file(vgs_file, 'w') as f:
                yaml.dump(ans, f)
Ejemplo n.º 10
0
def generate_and_sim(prj, generate=True):
    ver_specs_fname = 'specs_verification/opamp_two_stage_1e8.yaml'
    sim_specs_fname = 'specs_verification/opamp_two_stage_1e8_sim.yaml'

    ver_specs = read_yaml(ver_specs_fname)
    ver_specs['measurements'][0]['find_cfb'] = False

    with open_file(sim_specs_fname, 'w') as f:
        yaml.dump(ver_specs, f)

    sim = DesignManager(prj, sim_specs_fname)
    sim.characterize_designs(generate=generate,
                             measure=True,
                             load_from_file=False)
    dsn_name = list(sim.get_dsn_name_iter())[0]
    summary = sim.get_result(dsn_name)['opamp_ac']

    print('result:')
    pprint.pprint(summary)
Ejemplo n.º 11
0
    def get_result(self, dsn_name):
        # type: (str) -> Dict[str, Any]
        """Returns the measurement result summary dictionary.

        Parameters
        ----------
        dsn_name : str
            the design name.

        Returns
        -------
        result : Dict[str, Any]
            the result dictionary.
        """
        fname = os.path.join(self._root_dir, dsn_name, self.specs['summary_fname'])
        with open_file(fname, 'r') as f:
            summary = yaml.load(f)

        return summary
Ejemplo n.º 12
0
 def get_state_output(self, state):
     # type: (str) -> Dict[str, Any]
     """Get the post-processed output of the given state."""
     with open_file(os.path.join(self.data_dir, '%s.yaml' % state),
                    'r') as f:
         return yaml.load(f)
Ejemplo n.º 13
0
def design_close_loop(prj, funity_min_first=None, max_iter=100):
    interp_method = 'spline'
    nch_conf_list = [
        'data/nch_w4_stack/specs.yaml',
    ]
    pch_conf_list = [
        'data/pch_w4_stack/specs.yaml',
    ]
    amp_specs_fname = 'specs_design/opamp_two_stage_1e8.yaml'
    ver_specs_fname = 'specs_verification/opamp_two_stage_1e8.yaml'
    iter_cnt = 0
    f_unit_min_sim = -1
    k_max = 2.0
    k_min = 1.1

    print('create transistor database')
    nch_db = MOSDBDiscrete(nch_conf_list, interp_method=interp_method)
    pch_db = MOSDBDiscrete(pch_conf_list, interp_method=interp_method)

    top_specs = read_yaml(amp_specs_fname)
    funity_dsn_targ = funity_targ = top_specs['dsn_specs']['f_unit']

    sim, dsn_info = None, None
    summary = None
    while f_unit_min_sim < funity_targ and iter_cnt < max_iter:
        print('Iteration %d, f_unit_dsn_targ = %.4g' %
              (iter_cnt, funity_dsn_targ))
        top_specs['dsn_specs']['f_unit'] = funity_dsn_targ
        if dsn_info is not None:
            top_specs['dsn_specs']['i1_min_size'] = dsn_info['i1_size']

        if funity_min_first is not None and iter_cnt == 0:
            generate = False
            f_unit_min_dsn = funity_min_first
        else:
            generate = True
            dsn = design(top_specs, nch_db, pch_db)
            dsn_info = dsn.get_dsn_info()
            f_unit_min_dsn = min(dsn_info['f_unit'])

            ver_specs = dsn.get_specs_verification(top_specs)

            with open_file(ver_specs_fname, 'w') as f:
                yaml.dump(ver_specs, f)

        sim = DesignManager(prj, ver_specs_fname)
        sim.characterize_designs(generate=generate,
                                 measure=True,
                                 load_from_file=False)
        dsn_name = list(sim.get_dsn_name_iter())[0]
        summary = sim.get_result(dsn_name)['opamp_ac']

        funity_list = summary['funity']

        print('Iteration %d, result:' % iter_cnt)
        pprint.pprint(summary)

        f_unit_min_sim = min(funity_list)
        k = funity_targ / f_unit_min_sim
        k_real = max(k_min, min(k, k_max))
        print('k = %.4g, k_real = %.4g' % (k, k_real))
        funity_dsn_targ = f_unit_min_dsn * k_real
        iter_cnt += 1

    print('close loop design done.  Final result:')
    pprint.pprint(summary)

    return dsn_info