def write_out_specs_yaml(self, summary_in: Dict[str, Any], yaml_fname: str) -> None: from bag.io.file import write_yaml dict_to_write = summary_in.copy() self.prep_lvshift_dict(dict_to_write, 'ctrl_lvshift') self.prep_lvshift_dict(dict_to_write, 'data_lvshift') self.prep_driver_dict(dict_to_write, 'driver') write_yaml(yaml_fname, dict_to_write) self.log(f'Wrote final layout specifications to {yaml_fname}.')
def main() -> None: config_fname, output_dir = parse_options() output_dir.mkdir(parents=True, exist_ok=True) config = read_yaml(config_fname) netlist_map, inc_list, prim_files = get_info(config, output_dir) netlist_map.update(netlist_map_default) result = { 'prim_files': prim_files, 'inc_list': inc_list, 'netlist_map': netlist_map, } write_yaml(output_dir / 'netlist_setup.yaml', result)
async def async_measure_performance( self, name: str, sim_dir: Path, sim_db: SimulationDB, dut: Optional[DesignInstance]) -> 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 ---------- name : str name of this measurement. sim_dir : Path simulation directory. sim_db : SimulationDB the simulation database object. dut : Optional[DesignInstance] the DUT to measure. Returns ------- output : Dict[str, Any] the last dictionary returned by process_output(). """ specs = self.specs out_rise: bool = specs.get('out_rise', True) out_fall: bool = specs.get('out_fall', True) fake: bool = specs.get('fake', False) wait_cycles: int = specs.get('wait_cycles', 0) add_src_res: bool = specs.get('add_src_res', False) extra_loads: Optional[Sequence[Mapping[str, Any]]] = specs.get( 'load_list', None) rs = 'r_src' if add_src_res else '' load_list: List[Mapping[str, Any]] = [ dict(pin=p_, type='cap', value='c_load') for p_ in self._out_list ] if extra_loads: load_list.extend(extra_loads) pulse_list = [ dict(pin=p_, tper='2*t_bit', tpw='t_bit', trf='t_rf', td='t_bit', pos=True, rs=rs) for p_ in self._in_list ] num_bits = 3 + 2 * wait_cycles tbm_specs, tb_params = setup_digital_tran(specs, dut, pulse_list=pulse_list, load_list=load_list, skip_src=True) save_set = set( chain(self._in_list, self._out_list, self._start_list, self._stop_list)) if add_src_res: save_set.update( (DigitalTranTB.get_r_src_pin(p_) for p_ in self._in_list)) tbm_specs['save_outputs'] = list(save_set) tbm = cast(DigitalTranTB, sim_db.make_tbm(DigitalTranTB, tbm_specs)) tbm.sim_params[ 't_sim'] = f't_rst+t_rst_rf/{tbm.trf_scale:.2f}+{num_bits}*t_bit' if fake: raise ValueError('fake mode is broken') else: results = await self._run_sim(name, sim_db, sim_dir, dut, tbm, tb_params, wait_cycles, out_rise, out_fall, is_mc=False) self.log(f'Measurement {name} done, recording results.') mc_params = specs.get('mc_params', {}) if mc_params: mc_name = f'{name}_mc' self.log('Starting Monte Carlo simulation') mc_tbm_specs = tbm_specs.copy() mc_tbm_specs['sim_envs'] = [specs.get('mc_corner', 'tt_25')] mc_tbm_specs['monte_carlo_params'] = mc_params mc_tbm = cast(DigitalTranTB, sim_db.make_tbm(DigitalTranTB, mc_tbm_specs)) mc_tbm.sim_params[ 't_sim'] = f't_rst+t_rst_rf/{mc_tbm.trf_scale:.2f}+{num_bits}*t_bit' mc_results = await self._run_sim(mc_name, sim_db, sim_dir, dut, mc_tbm, tb_params, wait_cycles, out_rise, out_fall, is_mc=True) results = dict( tran=results, mc=mc_results, ) write_yaml(sim_dir / f'{name}.yaml', results) return results
async def async_design(self, **kwargs: Any) -> Mapping[str, Any]: """This function extracts design parameters and calls sub-hierarchy design functions. It passes parameters between the results of each sub-hierarchy design to accomplish logical-effort based design. If layout parameters are passed in through the design specs, they will be returned instead of running the design procedures. Passed in kwargs are the same as self.dsn_specs. Parameters ---------- kwargs: Any data_lv_specs: Mapping[str, Any] Data Level Shifter design parameters se_en_specs: Mapping[str, Any] Single Ended to Differential design parameters ctrl_lv_specs: Mapping[str, Any] Control / Enable Level Shifters design parameters por_lv_specs: Mapping[str, Any] POR Level Shifter design parameters c_load: float Target load capacitance on odat c_odat_async: float Target load capacitance on odat_async c_por_vccl_tx: float Target load capacitance from the TX on por_vccl fanout_odat_async: float Target fanout for odat_async output inverter w_n_inv: Union[int, float] NMOS width for POR Level Shifter input buffer w_p_inv: Union[int, float] PMOS width for POR Level Shifter input buffer yaml_file: str Output file location to write designed generator parameters data_lv_results: Optional[Mapping[str, Any]] If provided, return these generator parameters instead of running the design procedures se_en_results: Optional[Mapping[str, Any]] If provided, return these generator parameters instead of running the design procedures ctrl_lv_results: Optional[Mapping[str, Any]] If provided, return these generator parameters instead of running the design procedures por_lv_results: Optional[Mapping[str, Any]] If provided, return these generator parameters instead of running the design procedures Below are global specs shared and passed to each of the designers w_min: Union[int, float] Minimum width w_res: Union[int, float] Width resolution c_unit_n: float Unit NMOS transistor capacitance for w=1, seg=1 c_unit_p: float Unit PMOS transistor capacitance for w=1, seg=1 dig_tran_specs: Mapping[str, Any] DigitalTranTB testbench specs search_params: Mapping[str, Any] Parameters used for capacitor size binary search search_step: int Binary search step size max_iter: int Maximum allowed iterations to search for converge in binary search buf_config: Mapping[str, Any] Buffer parameters, used in DigitalTranTB and capacitor size search sign_off_envs: Sequence[str] Corners used for sign off sup_values: Mapping[str, Any] Per-corner supply values tile_name: str Tile name for layout. tile_specs: Mapping[str, Any] Tile specifications for layout. ridx_n: int NMOS transistor row ridx_p: int PMOS transistor Row Returns ------- ans: Mapping[str, Any] Design summary """ specs = self.dsn_specs yaml_file: str = specs.get('yaml_file', '') data_lv_results = await self.design_data_lv() se_en_results = await self.design_se_en(data_lv_results['c_in']) c_en = max(se_en_results['c_en_se'], se_en_results['c_en_match']) ctrl_lv_results = await self.design_ctrl_lv(c_en) c_por_vccl = max(ctrl_lv_results['c_rst_out'], ctrl_lv_results['c_rst_casc']) * 2 c_por_vccl += self.dsn_specs['c_por_vccl_tx'] por_lv_results = await self.design_por_lv(c_por_vccl) c_por_core = max(data_lv_results['c_rst_out'], data_lv_results['c_rst_casc']) * 2 c_por_core += por_lv_results['c_in'] buf_por_lv_params = self.get_buf_params(c_por_core) inv_params = self.get_inv_params(self.dsn_specs['c_odat_async'], self.dsn_specs['fanout_odat_async']) se_params = se_en_results['se_params'] match_params = se_en_results['match_params'] data_lv_params = data_lv_results['lv_params'] por_lv_params = por_lv_results['lv_params'] for table in [se_params, match_params, data_lv_params, por_lv_params]: table.pop('pinfo', None) table.pop('ridx_n', None) table.pop('ridx_p', None) rx_params = dict( se_params=se_params, match_params=match_params, inv_params=inv_params, data_lv_params=data_lv_params, ctrl_lv_params=ctrl_lv_results['dut_params']['lv_params'], por_lv_params=por_lv_params, buf_ctrl_lv_params=ctrl_lv_results['dut_params']['in_buf_params'], buf_por_lv_params=buf_por_lv_params, ) ans = dict(rx_params=rx_params, ) if yaml_file: write_yaml(self.work_dir / yaml_file, ans) return ans
async def async_measure_performance(self, name: str, sim_dir: Path, sim_db: SimulationDB, dut: Optional[DesignInstance]) -> Dict[str, Any]: """ A coroutine that performs measurement. This will... 1. Acquire every sweep point/configuration (get_swp_list) 2. Run simulation for each sweep point (_run_sim) 3. Aggregate results and return (aggregate_results) Parameters ---------- name : str name of measurement sim_dir : Path simulation directory sim_db : SimulationDB the simulation database object dut : Optional[DesignInstance] the DUT to measure Returns ------- results dictionary """ assert len(self._sim_envs) > 0 self.tbm_dict: Dict[str, TestbenchManager] = {k: None for k in self.tbm_order} res = {} for idx, tbm_name in enumerate(self.tbm_order): if not self._run_tbm[tbm_name]: continue tbm = self.add_tbm(tbm_name) sim_results = await sim_db.async_simulate_tbm_obj(tbm_name, sim_dir / tbm_name, dut, tbm, tb_params={}) data = sim_results.data ss_fname = str(sim_dir / 'ss_params.hdf5') if tbm_name == 'ibias': tbm: MOSIdTB vgs_range = tbm.get_vgs_range(data) if self._run_tbm['sp']: self.specs['tbm_specs']['sp']['vgs_range'] = vgs_range if self._run_tbm['noise']: self.specs['tbm_specs']['noise']['vgs_range'] = vgs_range self.commit() res['vgs_range'] = vgs_range elif tbm_name == 'sp': tbm: MOSSPTB ss_params = tbm.get_ss_params(data) # save SS parameters save_sim_results(ss_params, ss_fname) res['ss_file'] = ss_fname elif tbm_name == 'noise': # TODO: needs to be verified tbm: MOSNoiseTB temp = self.specs['noise_temp_kelvin'] fstart = self.specs['noise_integ_fstart'] fstop = self.specs['noise_integ_fstop'] scale = self.specs.get('noise_integ_scale', 1.0) ss_params = load_sim_file(ss_fname) ss_params = tbm.get_integrated_noise(data, ss_params, temp, fstart, fstop, scale=scale) save_sim_results(ss_params, ss_fname) res['ss_file'] = ss_fname else: raise ValueError(f"Unknown tbm name {tbm_name}") write_yaml(sim_dir / f'{name}.yaml', res) return res
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