async def get_driver_cin(self, driver_summary, vdd_out, c_ext, cap_in_search_params, dig_buf_params, dig_tbm_specs, num_units) -> dict: pin_names = [ 'din', 'tristate', 'tristateb', 'weak_pden', 'weak_puenb', 'n_enb_drv<0>', 'n_enb_drv<1>', 'p_en_drv<0>', 'p_en_drv<1>' ] helper = GatherHelper() dut = await self.async_new_dut('cin_driver_dut', STDCellWrapper, driver_summary['dut_params']) for pin in pin_names: helper.append( self._get_driver_input_cap(pin, driver_summary['dut_params'], vdd_out, c_ext, num_units, dig_tbm_specs, dig_buf_params, cap_in_search_params, dut)) results = await helper.gather_err() cdict = {} for idx, pin in enumerate(pin_names): # extract data cap and max of all ctrl pin caps cin = results[idx] if pin == 'din': cdict['data'] = cin else: c_cur = cdict.get('ctrl', -float('inf')) if cin > c_cur: cdict['ctrl'] = cin return cdict
async def async_measure_performance(self, name: str, sim_dir: Path, sim_db: SimulationDB, dut: Optional[DesignInstance]) -> Mapping[str, Any]: helper = GatherHelper() for idx in range(2): helper.append(self.async_meas_case(name, sim_dir, sim_db, dut, idx)) meas_results = await helper.gather_err() ans = self.compute_passives(meas_results) return ans
async def _measure_times(self, sim_envs: Sequence[str], tbm_dict: Dict[str, DigitalTranTB], dut_params: Param, tbm_params: Mapping[str, Any], tbit: float, nbits: int, name: str) -> Dict[str, Any]: gen_params = dict( cls_name=PhaseInterpolatorWithDelay.get_qualified_name(), params=dut_params, ) dut = await self.async_new_dut('phase_interp', GenericWrapper, gen_params) helper = GatherHelper() for corner in sim_envs: helper.append( self._measure_times_at_corner(tbm_dict[corner], dut, tbm_params, tbit, nbits, f'{name}_{corner}')) results = await helper.gather_err() dct = { k: [] for k in ['tdrs', 'tdfs', 'tdrs_dc', 'tdfs_dc', 'tdr_step', 'tdf_step'] } max_steps, min_steps, max_dly = [], [], [] for idx, corner in enumerate(sim_envs): tdr, tdf, tdr_dc, tdf_dc = results[idx] max_dly.append(max(np.min(tdr), np.min(tdf))) tdr = np.vstack((tdr, (tdr[0] + tdr_dc)[None, ...])) tdf = np.vstack((tdf, (tdf[0] + tdf_dc)[None, ...])) tdr_step = np.diff(tdr, axis=0) tdf_step = np.diff(tdf, axis=0) dct['tdr_step'].append(tdr_step) dct['tdf_step'].append(tdf_step) dct['tdrs'].append(tdr) dct['tdfs'].append(tdf) dct['tdrs_dc'].append(tdr_dc) dct['tdfs_dc'].append(tdf_dc) max_steps.append(max(np.max(tdr_step), np.max(tdf_step))) min_steps.append(min(np.min(tdr_step), np.min(tdf_step))) for k in [ 'tdrs', 'tdfs', 'tdrs_dc', 'tdfs_dc', 'tdr_step', 'tdf_step' ]: dct[k] = np.array(dct[k]) max_idx = cast(int, np.argmax(np.array(max_steps))) min_idx = cast(int, np.argmin(np.array(min_steps))) dct['max_corner'] = sim_envs[max_idx] dct['min_corner'] = sim_envs[min_idx] dct['max_step'] = max_steps[max_idx] dct['min_step'] = min_steps[min_idx] dct['max_dly'] = max(max_dly) return dct
async def _measure_times(self, nand_outer_seg: int, nand_inner_seg: int, stack: int, ncodes: int, cload: float, num_core: int, nrows: int, ncols: int, pinfo: Mapping[str, Any], sim_params: Mapping[str, Any]): dut_params = self._get_dut_params(nand_outer_seg, nand_inner_seg, stack, ncodes, num_core, nrows, ncols, pinfo) dut = await self.async_new_dut('dly_line_chain', STDCellWrapper, dut_params) tper = sim_params['tper'] ncycles = sim_params['ncycles'] sim_id_pref = f'dly_no{nand_outer_seg}_ni{nand_inner_seg}_s{stack}' helper = GatherHelper() for code in range(0, ncodes - 1): tbm_params = self._get_tbm_params(code, ncodes, cload, sim_params) sim_id = f'{sim_id_pref}_c{code}' helper.append( self._get_delay(sim_id, dut, tbm_params, t_start=tper * (ncycles - 1), t_stop=tper * ncycles)) ret_list = await helper.gather_err() tdr_list, tdf_list = [x[0] for x in ret_list], [x[1] for x in ret_list] res_arr = dict() tdr_arr = np.stack(tdr_list, axis=-1) res_arr['tdr_arr'] = tdr_arr tdf_arr = np.stack(tdf_list, axis=-1) res_arr['tdf_arr'] = tdf_arr tdr_per_code = np.diff(tdr_arr, axis=-1)[..., ::2] res_arr['tdr_per_code'] = tdr_per_code tdf_per_code = np.diff(tdf_arr, axis=-1)[..., ::2] res_arr['tdf_per_code'] = tdf_per_code tdr_max = np.max(tdr_per_code) res_arr['tdr_max'] = tdr_max tdf_max = np.max(tdf_per_code) res_arr['tdf_max'] = tdf_max tdr_min = np.min(tdr_per_code) res_arr['tdr_min'] = tdr_min tdf_min = np.min(tdf_per_code) res_arr['tdf_min'] = tdf_min res_arr['td_max'] = max(tdr_max, tdf_max) res_arr['td_min'] = min(tdr_min, tdf_min) return res_arr
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
async def _design_chain_step( self, nand_outer_seg: int, nand_inner_seg: int, stack: int, ncodes: int, cload: float, num_core: int, nrows: int, ncols: int, pinfo: Mapping[str, Any], sim_params: Mapping[str, Any]) -> Tuple[float, float]: mid = ncodes // 2 dut_params = self._get_dut_params(nand_outer_seg, nand_inner_seg, stack, ncodes, num_core, nrows, ncols, pinfo) # dut = await self.async_new_dut('dly_line_chain', STDCellWrapper, dut_params) dut = await self.async_new_dut('dly_line_chain', GenericWrapper, dut_params) tper = sim_params['tper'] ncycles = sim_params['ncycles'] sim_id_pref = f'dly_no{nand_outer_seg}_ni{nand_inner_seg}_s{stack}' helper = GatherHelper() for code in [0, 1, mid, mid + 1, ncodes - 2, ncodes - 1]: tbm_params = self._get_tbm_params(code, ncodes, cload, sim_params) sim_id = f'{sim_id_pref}_c{code}' helper.append( self._get_delay(sim_id, dut, tbm_params, t_start=tper * (ncycles - 1), t_stop=tper * ncycles)) ret_list = await helper.gather_err() tdr_list, tdf_list = [x[0] for x in ret_list], [x[1] for x in ret_list] tdr_arr = np.stack(tdr_list, axis=-1) tdf_arr = np.stack(tdf_list, axis=-1) tdr_per_code = np.diff(tdr_arr, axis=-1)[..., ::2] tdf_per_code = np.diff(tdf_arr, axis=-1)[..., ::2] tdr_max = np.max(tdr_per_code) tdf_max = np.max(tdf_per_code) tdr_min = np.min(tdr_per_code) tdf_min = np.min(tdf_per_code) td_max = max(tdr_max, tdf_max) td_min = min(tdr_min, tdf_min) return td_max, td_min
async def sign_off(self, se_params: Dict[str, Any]) -> Mapping[str, Any]: sign_off_envs: Sequence[str] = self.dsn_specs['sign_off_envs'] dut = await self.async_wrapper_dut('SE_TO_DIFF', SingleToDiff, se_params) gatherer = GatherHelper() for sim_env in sign_off_envs: mm_specs = self._td_specs.copy() mm_specs['tbm_specs'] = tbm_specs = mm_specs['tbm_specs'].copy() tbm_specs['sim_envs'] = [sim_env] mm = self.make_mm(CombLogicTimingMM, mm_specs) gatherer.append( self.async_simulate_mm_obj( f'sign_off_{sim_env}_{dut.cache_name}', dut, mm)) result_list = await gatherer.gather_err() ans = {} pin_list = ['outp', 'outn'] for sim_env, meas_result in zip(sign_off_envs, result_list): timing_data = meas_result.data['timing_data'] cur_results = {} for pin_name in pin_list: data = timing_data[pin_name] cur_results[f'td_{pin_name}'] = (data['cell_fall'], data['cell_rise']) cur_results[f'trf_{pin_name}'] = (data['fall_transition'], data['rise_transition']) td_outp = cur_results['td_outp'] td_outn = cur_results['td_outn'] td_avg_rise = (td_outp[1] + td_outn[0]) / 2 td_avg_fall = (td_outp[0] + td_outn[1]) / 2 err_rise = abs(td_outp[1] - td_outn[0]) / 2 / td_avg_rise err_fall = abs(td_outp[0] - td_outn[1]) / 2 / td_avg_fall cur_results['td_out_avg'] = (td_avg_fall, td_avg_rise) cur_results['td_err'] = (err_fall, err_rise) ans[sim_env] = cur_results return ans
async def sign_off(self, se_params: Mapping[str, Any], match_params: Mapping[str, Any] ) -> Mapping[str, Any]: sign_off_envs: Sequence[str] = self.dsn_specs['sign_off_envs'] gatherer = GatherHelper() dut_se = await self.get_dut(se_params, False) dut_match = await self.get_dut(match_params, True) for sim_env in sign_off_envs: gatherer.append(self.get_delays_dut(dut_se, False, sim_env)) gatherer.append(self.get_delays_dut(dut_match, True, sim_env)) result_list = await gatherer.gather_err() ans = {} for idx, sim_env in enumerate(sign_off_envs): se_data = result_list[2 * idx] match_data = result_list[2 * idx + 1] ans[sim_env] = cur_result = {} for name, td_data in [('se_en', se_data), ('match', match_data)]: td_outp = td_data.outp td_outn = td_data.outn td_rise_avg = td_data.td_rise_avg td_fall_avg = td_data.td_fall_avg err_rise = abs(td_outp[1] - td_outn[0]) / 2 / td_rise_avg err_fall = abs(td_outp[0] - td_outn[1]) / 2 / td_fall_avg cur_result[name] = dict( td_outp=td_outp, td_outn=td_outn, td_avg=(td_fall_avg, td_rise_avg), td_err=(err_fall, err_rise), ) return ans
async def async_measure_performance( self, name: str, sim_dir: Path, sim_db: SimulationDB, dut: Optional[DesignInstance]) -> Dict[str, Any]: specs = self.specs flop_params: Mapping[str, Any] = specs['flop_params'] tbm_cls_val: Union[str, Type[FlopTimingBase]] = specs['tbm_cls'] t_rf_list: Sequence[float] = specs['t_rf_list'] t_clk_rf_list: Sequence[float] = specs['t_clk_rf_list'] t_clk_rf_first: bool = specs['t_clk_rf_first'] fake: bool = specs.get('fake', False) use_dut: bool = specs.get('use_dut', True) meas_dut = dut if use_dut else None tbm_cls = cast(Type[FlopTimingBase], import_class(tbm_cls_val)) mode_list = tbm_cls.get_meas_modes(flop_params) out_mode_list = tbm_cls.get_output_meas_modes(flop_params) # output timing measurement self.log('Measuring output delays') timing_table = {} gatherer = GatherHelper() for out_mode in out_mode_list: gatherer.append( self.get_out_timing(name, sim_db, meas_dut, sim_dir, tbm_cls, out_mode, fake, timing_table)) self.log('Measuring input constraints') clk_len = len(t_clk_rf_list) in_len = len(t_rf_list) arr_shape = (clk_len, in_len) if t_clk_rf_first else (in_len, clk_len) for ck_idx, t_clk_rf in enumerate(t_clk_rf_list): for rf_idx, t_rf in enumerate(t_rf_list): for meas_mode in mode_list: coro = self.get_in_timing(name, sim_db, meas_dut, sim_dir, meas_mode, fake, t_clk_rf, t_rf, ck_idx, rf_idx, arr_shape, t_clk_rf_first, timing_table) gatherer.append(coro) await gatherer.gather_err() ans = {key: list(val.values()) for key, val in timing_table.items()} return ans
async def async_measure_performance( self, name: str, sim_dir: Path, sim_db: SimulationDB, dut: Optional[DesignInstance]) -> Dict[str, Any]: specs = self.specs in_cap_min: float = specs['in_cap_min_default'] out_max_trf: float = specs['out_max_trf'] out_min_fanout: float = specs['out_min_fanout'] in_cap_table: Mapping[str, float] = specs['in_cap_table'] out_io_info_table: Mapping[str, Mapping[str, Any]] = specs['out_io_info_table'] custom_meas: Mapping[str, Mapping[str, Any]] = specs['custom_meas'] # setup input capacitance measurements ans = {} out_io_pins = [] gatherer = GatherHelper() for pin_name, term_type in dut.sch_master.pins.items(): basename, bus_range = parse_cdba_name(pin_name) if bus_range is None: ans[pin_name] = pin_info = {} if term_type is TermType.input: gatherer.append( self._measure_in_cap(name, sim_dir, sim_db, dut, pin_name, in_cap_table, pin_info)) else: out_io_pins.append(pin_name) else: for bus_idx in bus_range: bit_name = get_bus_bit_name(basename, bus_idx, cdba=True) ans[bit_name] = pin_info = {} if term_type is TermType.input: gatherer.append( self._measure_in_cap(name, sim_dir, sim_db, dut, bit_name, in_cap_table, pin_info)) else: out_io_pins.append(bit_name) # record input capacitances if gatherer: in_cap_min = min((val for val in await gatherer.gather_err() if val is not None)) # get parameters needed for output pin measurement out_cap_min = in_cap_min * out_min_fanout # compute inout and output pin cap/timing information gatherer.clear() for bit_name in out_io_pins: pin_info = out_io_info_table.get(bit_name, None) if pin_info is None: continue cap_info: Mapping[str, Any] = pin_info.get('cap_info', None) tinfo_list: Optional[Sequence[Mapping[str, Any]]] = pin_info.get( 'timing_info', None) output_table = ans[bit_name] if cap_info is not None: related: str = cap_info.get('related', '') max_cap: Optional[float] = cap_info.get('max_cap', None) max_trf: float = cap_info.get('max_trf', out_max_trf) cond: Mapping[str, int] = cap_info.get('cond', {}) gatherer.append( self._measure_out_cap(name, sim_dir, sim_db, dut, bit_name, related, max_cap, max_trf, cond, out_cap_min, output_table)) if tinfo_list is not None: output_table['timing'] = timing_output = [] for idx, tinfo in enumerate(tinfo_list): related: str = tinfo['related'] sense_str: str = tinfo['sense'] cond: Mapping[str, int] = tinfo.get('cond', {}) timing_type: str = tinfo.get('timing_type', 'combinational') zero_delay: bool = tinfo.get('zero_delay', False) data: Optional[Mapping[str, Any]] = tinfo.get('data', None) related_str = cdba_to_unusal(related) out_str = cdba_to_unusal(bit_name) sim_id = f'comb_delay_{related_str}_{out_str}_{idx}' gatherer.append( self._measure_delay(name, sim_id, sim_dir, sim_db, dut, bit_name, related, sense_str, cond, timing_type, zero_delay, data, timing_output)) # add custom and flop measurements for meas_name, meas_params in custom_meas.items(): meas_cls: str = meas_params['meas_class'] meas_specs: Mapping[str, Any] = meas_params['meas_specs'] gatherer.append( self._measure_custom(name, sim_dir, sim_db, dut, meas_name, meas_cls, meas_specs, ans)) for seq_name, seq_mm in self._seq_mm_table.items(): gatherer.append( self._measure_flop(name, sim_dir, sim_db, dut, seq_name, seq_mm, ans)) # run all simulation in parallel await gatherer.run() return ans
async def _design_for_mc(self, outer_seg: int, inner_seg: int, stack: int, cload: float, std_dev_max: float, num_core: int, pinfo: Mapping[str, Any], sim_params: Mapping[str, Any], mc_dsn_env: str, mc_params: Mapping[str, Any], ncodes: int, max_iter: int = 5) -> Tuple[int, int]: # ncodes in this part can be a small value (i.e. 3) global_info = self.dsn_tech_info indx = 0 cur_std_max = float('inf') n_factor = 1 tstart = sim_params['tper'] * (sim_params['ncycles'] - 1) tstop = sim_params['tper'] * sim_params['ncycles'] # finish if maximum number of iteration reached while cur_std_max > std_dev_max: if indx > max_iter: raise ValueError( f'Reached the maximum niter {max_iter}, but have not reached ' f'std spec') _factor = int(np.ceil(outer_seg * n_factor)) / outer_seg outer_seg = int(np.ceil(outer_seg * _factor)) inner_seg = int(np.ceil(inner_seg * _factor)) cload *= _factor self.log( f'Computed fudge factor: {n_factor}, actual factor: {_factor}') self.log(f'New outer NAND Seg: {outer_seg}') self.log(f'New inner NAND Seg: {inner_seg}') self.log(f'New cload: {cload}') # put all cells in a single row dut_params = self._get_dut_params(outer_seg, inner_seg, stack, ncodes=ncodes, num_core=num_core, nrows=1, ncols=ncodes + 2, pinfo=pinfo) dut = await self.async_new_dut('dly_line_chain', STDCellWrapper, dut_params) helper = GatherHelper() for code in range(ncodes): mc_sim_params = dict(deepcopy(sim_params)) mc_sim_params['sim_envs'] = global_info['dsn_envs'][ mc_dsn_env]['mc_env'] mc_tb_params = self._get_tbm_params(code, ncodes, cload, mc_sim_params) mc_tb_params['monte_carlo_params'] = mc_params sim_id = f'dly_no{outer_seg}_ni{inner_seg}_s{stack}_mc{indx}_c{code}' self.log("Running Monte Carlo on design") helper.append( self._get_delay(sim_id, dut, mc_tb_params, t_start=tstart, t_stop=tstop)) ret_list = await helper.gather_err() tdr_list, tdf_list = [x[0] for x in ret_list], [x[1] for x in ret_list] tdr = np.stack(tdr_list, axis=0) tdf = np.stack(tdf_list, axis=0) steps_r = np.diff(tdr, axis=0).squeeze() steps_f = np.diff(tdf, axis=0).squeeze() sdr = np.std(steps_r) sdf = np.std(steps_f) cur_std_max = max(sdr, sdf) print('=' * 80) self.log( f"Standard deviation of the rising delay: {sdr}, Spec = {std_dev_max}" ) self.log( f"Standard deviation of the falling delay: {sdf}, Spec = {std_dev_max}" ) print('=' * 80) diff_factor = cur_std_max / std_dev_max n_factor = diff_factor**2 indx += 1 return outer_seg, inner_seg
async def _get_init_rc(self) -> RCData: specs = self.dsn_specs inv_char_params: Mapping[str, Any] = specs['inv_char_params'] pg_char_params: Mapping[str, Any] = specs['pg_char_params'] inv_params = dict( pinfo=self._pinfo, seg=inv_char_params['seg'], w_p=inv_char_params['w_p'], w_n=inv_char_params['w_n'], ) pg_params = dict( pinfo=self._pinfo, seg=pg_char_params['seg'], w_p=pg_char_params['w_p'], w_n=pg_char_params['w_n'], ) # get initial inverter and passgate DUT gatherer = GatherHelper() gatherer.append(self.async_wrapper_dut('INV_CHAR', InvCore, inv_params)) gatherer.append( self.async_wrapper_dut('PG_CHAR', PassGateCore, pg_params)) dut_inv, dut_pg = await gatherer.gather_err() # get inverter and passgate RC inv_mm = self.make_mm(RCDelayCharMM, self._rc_inv_specs) pg_mm = self.make_mm(PassGateRCDelayCharMM, self._rc_pg_specs) gatherer.clear() gatherer.append( self.async_simulate_mm_obj('rc_inv_char', dut_inv, inv_mm)) gatherer.append(self.async_simulate_mm_obj('rc_pg_char', dut_pg, pg_mm)) inv_result, pg_result = await gatherer.gather_err() w_inv = inv_params['w_n'] * inv_params['seg'] w_pg = pg_params['w_n'] * pg_params['seg'] rc_inv = _format_inv_rc(inv_result.data, w_inv) rc_pg = _format_pg_rc(pg_result.data, w_pg) return RCData(rc_inv, rc_pg)
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
async def async_measure_performance( self, name: str, sim_dir: Path, sim_db: SimulationDB, dut: Optional[DesignInstance]) -> Dict[str, Any]: specs = self.specs in_pin: str = specs['in_pin'] out_pin: str = specs['out_pin'] c_in: float = specs.get('c_in', 0) t_step_min: float = specs.get('t_step_min', 0.1e-12) plot: bool = specs.get('plot', False) cin_mm = sim_db.make_mm(CombLogicTimingMM, self._cin_specs) td_mm = sim_db.make_mm(CombLogicTimingMM, self._td_specs) gatherer = GatherHelper() gatherer.append( sim_db.async_simulate_mm_obj(f'{name}_cin', sim_dir / 'cin', dut, cin_mm)) gatherer.append( sim_db.async_simulate_mm_obj(f'{name}_td', sim_dir / 'td', dut, td_mm)) cin_output, td_output = await gatherer.gather_err() t_unit = 10 * t_step_min sim_envs = cin_output.data['sim_envs'] r_src = cin_output.data['sim_params']['r_src'] cin_timing = cin_output.data['timing_data'][in_pin] td_cin_rise = cin_timing['cell_rise'] td_cin_fall = cin_timing['cell_fall'] rin_rise, cin_rise = self.fit_rc_in(td_cin_rise, r_src, c_in, t_unit) rin_fall, cin_fall = self.fit_rc_in(td_cin_fall, r_src, c_in, t_unit) td_timing = td_output.data['timing_data'][out_pin] c_load = td_output.data['sim_params']['c_load'] td_out_rise = td_timing['cell_rise'] td_out_fall = td_timing['cell_fall'] rout_rise, cout_rise = self.fit_rc_out(td_out_rise, c_load, t_unit) rout_fall, cout_fall = self.fit_rc_out(td_out_fall, c_load, t_unit) if plot: from matplotlib import pyplot as plt if len(sim_envs) > 100: raise ValueError('Can only plot with num. sim_envs < 100') for idx in range(len(sim_envs)): ri_r = rin_rise[idx] ri_f = rin_fall[idx] ci_r = cin_rise[idx] ci_f = cin_fall[idx] ro_r = rout_rise[idx] ro_f = rout_fall[idx] co_r = cout_rise[idx] co_f = cout_fall[idx] rs = r_src[idx, ...] cl = c_load[idx, ...] td_cin_r = td_cin_rise[idx, ...] td_cin_f = td_cin_fall[idx, ...] td_out_r = td_out_rise[idx, ...] td_out_f = td_out_fall[idx, ...] plt.figure(idx * 100 + 1) plt.title(f'{sim_envs[idx]} c_in') plt.plot(rs, td_cin_r, 'bo', label='td_rise') plt.plot(rs, td_cin_f, 'ko', label='td_fall') plt.plot(rs, np.log(2) * rs * (ci_r + c_in) + ri_r * ci_r, '-r', label='td_rise_fit') plt.plot(rs, np.log(2) * rs * (ci_f + c_in) + ri_f * ci_f, '-g', label='td_fall_fit') plt.legend() plt.figure(idx * 100 + 2) plt.title(f'{sim_envs[idx]} rc_out') plt.plot(cl, td_out_r, 'bo', label='td_rise') plt.plot(cl, td_out_f, 'ko', label='td_fall') plt.plot(cl, ro_r * (co_r + cl), '-r', label='td_rise_fit') plt.plot(cl, ro_f * (co_f + cl), '-g', label='td_fall_fit') plt.legend() plt.show() ans = dict( sim_envs=sim_envs, r_in=(rin_fall, rin_rise), c_in=(cin_fall, cin_rise), c_out=(cout_fall, cout_rise), r_out=(rout_fall, rout_rise), ) self.log(f'Measurement {name} done, result:\n{pprint.pformat(ans)}') write_yaml(sim_dir / f'{name}.yaml', ans) return ans