Beispiel #1
0
    def initialize(self, sim_db: SimulationDB,
                   dut: DesignInstance) -> Tuple[bool, MeasInfo]:
        specs = self.specs
        in_pin: str = specs['in_pin']
        out_pin: str = specs['out_pin']
        fake: str = specs.get('fake', False)

        if fake:
            return True, MeasInfo(
                'done', dict(cap=100.0e-15, tr=20.0e-12, tf=20.0e-12))

        load_list = [dict(pin=out_pin, type='cap', value='c_load')]
        wrapper_params = get_digital_wrapper_params(specs, dut, [in_pin])
        mm_specs = {
            k: specs[k]
            for k in
            ['in_pin', 'out_pin', 'max_trf', 'search_params', 'tbm_specs']
        }
        mm_specs['adj_name'] = 'c_load'
        mm_specs['adj_sign'] = True
        mm_specs['use_dut'] = True
        mm_specs['wrapper_params'] = wrapper_params
        mm_specs['load_list'] = load_list
        self._mm = sim_db.make_mm(MaxRiseFallTime, mm_specs)

        return False, MeasInfo('max_trf', {})
Beispiel #2
0
    async def _measure_in_cap(self, name: str, sim_dir: Path,
                              sim_db: SimulationDB,
                              dut: Optional[DesignInstance], pin_name: str,
                              in_cap_table: Mapping[str, float],
                              output_table: Dict[str, Any]) -> float:
        cap_range: float = self.specs['in_cap_range_scale']
        if self.fake:
            cap_rise = cap_fall = in_cap_table[pin_name]
        else:
            sim_id = f'cap_in_{cdba_to_unusal(pin_name)}'

            cur_specs = self._cin_specs.copy()
            cur_specs['in_pin'] = pin_name

            mm = sim_db.make_mm(CapDelayMatch, cur_specs)
            mm_result = await sim_db.async_simulate_mm_obj(
                f'{name}_{sim_id}', sim_dir / sim_id, dut, mm)
            mm_data = mm_result.data
            cap_rise = mm_data['cap_rise']
            cap_fall = mm_data['cap_fall']

        cap = (cap_rise + cap_fall) / 2
        cap_rise_range = [
            cap_rise * (1 - cap_range), cap_rise * (1 + cap_range)
        ]
        cap_fall_range = [
            cap_fall * (1 - cap_range), cap_fall * (1 + cap_range)
        ]
        output_table['cap_dict'] = dict(cap=cap,
                                        cap_rise=cap_rise,
                                        cap_fall=cap_fall,
                                        cap_rise_range=cap_rise_range,
                                        cap_fall_range=cap_fall_range)
        return cap
    def init_search(
        self, sim_db: SimulationDB, dut: DesignInstance
    ) -> Tuple[TestbenchManager, Mapping[str, Any], Mapping[str, Mapping[
            str, Any]], Mapping[str, Any], bool, bool]:
        specs = self.specs
        adj_name: str = specs['adj_name']
        in_pin: str = specs['in_pin']
        out_pin: str = specs['out_pin']
        search_params: Mapping[str, Any] = specs['search_params']
        use_dut: bool = specs.get('use_dut', True)
        load_list: Sequence[Mapping[str, Any]] = specs.get('load_list', [])

        # create pulse list
        pulse_list = [
            dict(pin=in_pin,
                 tper='2*t_bit',
                 tpw='t_bit',
                 trf='t_rf',
                 td='t_bit',
                 pos=True)
        ]

        tbm_specs, tb_params = setup_digital_tran(specs,
                                                  dut,
                                                  pulse_list=pulse_list,
                                                  load_list=load_list)
        tbm_specs['save_outputs'] = [out_pin]
        tbm = cast(DigitalTranTB, sim_db.make_tbm(DigitalTranTB, tbm_specs))
        tbm.sim_params['t_sim'] = f'{tbm.t_rst_end_expr}+3*t_bit'

        return tbm, tb_params, {adj_name: search_params}, {}, False, use_dut
Beispiel #4
0
    def init_search(
        self, sim_db: SimulationDB, dut: DesignInstance
    ) -> Tuple[TestbenchManager, Mapping[str, Any], Mapping[str, Mapping[
            str, Any]], Mapping[str, Any], bool, bool]:
        specs = self.specs
        adj_name: str = specs['adj_name']
        search_params: Mapping[str, Any] = specs['search_params']
        pulse_list: Sequence[Mapping[str, Any]] = specs['pulse_list']
        adj_params: Mapping[str, Any] = specs['adj_params']
        self._value: Optional[float] = specs.get('ref_delay', None)
        use_dut: bool = specs.get('use_dut', True)
        load_list: Sequence[Mapping[str, Any]] = specs.get('load_list', [])

        # get output list
        save_outputs = [adj_params['in_name'], adj_params['out_name']]
        if self._value is None:
            ref_params: Mapping[str, Any] = specs['ref_params']
            save_outputs.append(ref_params['in_name'])
            save_outputs.append(ref_params['out_name'])

        tbm_specs, tb_params = setup_digital_tran(specs,
                                                  dut,
                                                  pulse_list=pulse_list,
                                                  load_list=load_list)
        tbm_specs['save_outputs'] = save_outputs
        tbm = cast(DigitalTranTB, sim_db.make_tbm(DigitalTranTB, tbm_specs))

        return tbm, tb_params, {adj_name: search_params}, {}, False, use_dut
Beispiel #5
0
    async def _measure_out_cap(self, name: str, sim_dir: Path, sim_db: SimulationDB,
                               dut: Optional[DesignInstance], pin_name: str, related: str,
                               max_cap: Optional[float], max_trf: float, cond: Mapping[str, int],
                               out_cap_min: float, output_table: Dict[str, Any]) -> None:
        out_cap_num_freq: int = self.specs['out_cap_num_freq']

        if max_cap is None:
            if not related:
                raise ValueError('No related pin specified for max output cap measurement.')

            if self.fake:
                max_cap = 200.0e-15
            else:
                sim_id = f'cap_out_{cdba_to_unusal(pin_name)}'

                cur_specs = self._cout_specs.copy()
                cur_specs['in_pin'] = related
                cur_specs['out_pin'] = pin_name
                cur_specs['max_trf'] = max_trf
                if cond:
                    pin_values = cur_specs['tbm_specs']['pin_values'].copy()
                    pin_values.update(cond)
                    update_recursive(cur_specs, pin_values, 'tbm_specs', 'pin_values')

                mm = sim_db.make_mm(CapMaxRiseFallTime, cur_specs)
                mm_result = await sim_db.async_simulate_mm_obj(f'{name}_{sim_id}', sim_dir / sim_id,
                                                               dut, mm)
                mm_data = mm_result.data
                max_cap = mm_data['cap']

        output_table['cap_dict'] = dict(
            cap_min=min(max_cap, out_cap_min),
            cap_max=max_cap,
            cap_max_table=[max_cap] * out_cap_num_freq,
        )
    def init_search(
        self, sim_db: SimulationDB, dut: DesignInstance
    ) -> Tuple[TestbenchManager, Mapping[str, Any], Mapping[str, Mapping[
            str, Any]], Mapping[str, Any], bool, bool]:
        neg_inf = float('-inf')

        specs = self.specs
        meas_mode: Union[str, FlopMeasMode] = specs['meas_mode']
        flop_params: Mapping[str, Any] = specs['flop_params']
        tbm_cls: Union[str, Type[FlopTimingBase]] = specs['tbm_cls']
        search_params: Mapping[str, Any] = specs['search_params']
        fake: bool = specs.get('fake', False)
        use_dut: bool = specs.get('use_dut', True)
        constraint_min: float = specs.get('constraint_min', neg_inf)
        sim_env_name: str = specs.get('sim_env_name', '')

        tbm_specs, tb_params = setup_digital_tran(specs,
                                                  dut,
                                                  meas_mode=meas_mode,
                                                  flop_params=flop_params,
                                                  sim_env_name=sim_env_name)

        tbm = cast(FlopTimingBase, sim_db.make_tbm(tbm_cls, tbm_specs))
        sim_params = tbm.sim_params
        t_clk_per = sim_params['t_clk_per']
        t_rf = sim_params['t_rf'] / tbm.trf_scale

        tol: float = search_params['tol']
        overhead_factor: float = search_params['overhead_factor']
        max_margin: float = search_params.get('max_margin', t_clk_per / 4)

        min_val = max_margin if fake else -max_margin
        # NOTE: max_timing_value makes sure PWL width is always non-negative
        max_timing_value = max_margin + t_rf
        if constraint_min > neg_inf:
            # perform unbounded binary search instead
            high = constraint_min if fake else None
            defaults = dict(low=constraint_min,
                            high=high,
                            tol=tol,
                            step=1.0e-12,
                            max_err=max_margin,
                            overhead_factor=overhead_factor,
                            single_first=True)
        else:
            defaults = dict(low=min_val,
                            high=max_margin,
                            tol=tol,
                            step=1.0e-12,
                            max_err=float('inf'),
                            overhead_factor=overhead_factor,
                            single_first=False)

        self._output_map = tbm.get_output_map(False)
        for var in tbm.timing_variables:
            sim_params[var] = max_timing_value

        adj_table = {k: defaults for k in self._output_map.keys()}
        return tbm, tb_params, adj_table, defaults, True, use_dut
Beispiel #7
0
    def initialize(self, sim_db: SimulationDB,
                   dut: DesignInstance) -> Tuple[bool, MeasInfo]:
        specs = self.specs
        vdd = specs['vdd']
        v_offset_map = specs['v_offset_map']

        gate_bias = self.default_gate_bias.copy()
        if 'gate_bias' in specs:
            gate_bias.update(specs['gate_bias'])

        specs['stack_pu'] = dut.sch_master.params['stack_p']
        specs['stack_pd'] = dut.sch_master.params['stack_n']
        self._dut = dut

        mos_mapping = specs['mos_mapping'][
            'lay' if specs['extract'] else 'sch']

        # Find all transistors that match the passed in transistor names
        # The passed in transistor name can match multiple "transistors" in the netlist
        # in cases like seg > 1 or stack > 1
        # TODO: allow for more complex name matching (e.g., via regex)
        for mos, term in mos_mapping.items():
            voff_list = [
                v for k, v in v_offset_map.items()
                if term in k and k.endswith('_d')
            ]
            if len(voff_list) == 0:
                raise ValueError(f"No matching transistor found for {term}")
            self._mos_mapping[mos] = voff_list

        for k, v in gate_bias.items():
            if v == 'full':
                gate_bias[k] = vdd if k == 'pd' else 0
            else:
                gate_bias = self._eval_expr(v)

        sup_values = dict(VDD=vdd,
                          pden=gate_bias['pd'],
                          puenb=gate_bias['pu'],
                          out=vdd / 2)

        tbm_specs = dict(**specs['tbm_specs'])
        tbm_specs['sweep_var'] = 'v_out'
        tbm_specs['sweep_options'] = dict(type='LINEAR')
        for k in ['pwr_domain', 'sim_params', 'pin_values']:
            if k not in tbm_specs:
                tbm_specs[k] = {}
        tbm_specs['dut_pins'] = list(dut.sch_master.pins.keys())
        tbm_specs['load_list'] = []
        tbm_specs['sup_values'] = sup_values

        # Set all internal DC sources to 0 (since these are just used for current measurements)
        tbm_specs['sim_params'].update({k: 0 for k in v_offset_map.values()})

        tbm = cast(DCTB, sim_db.make_tbm(DCTB, tbm_specs))
        self._tbm_info = tbm, {}

        return False, MeasInfo('pd', {})
    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']
        buf_params: Optional[Mapping[str, Any]] = specs.get('buf_params', None)

        if buf_params is None:
            buf_config: Mapping[str, Any] = specs['buf_config']
            lch: int = buf_config['lch']
            w_p: int = buf_config['w_p']
            w_n: int = buf_config['w_n']
            th_p: str = buf_config['th_p']
            th_n: str = buf_config['th_n']
            cinv_unit: float = buf_config['cinv_unit']
            cin_guess: float = buf_config['cin_guess']
            fanout_in: float = buf_config['fanout_in']
            fanout_buf: float = buf_config.get('fanout_buf', 4)

            seg1 = int(round(max(cin_guess / fanout_in / cinv_unit, 1.0)))
            seg0 = int(round(max(seg1 / fanout_buf, 1.0)))
            buf_params = dict(
                export_pins=True,
                inv_params=[
                    dict(lch=lch,
                         w_p=w_p,
                         w_n=w_n,
                         th_p=th_p,
                         th_n=th_n,
                         seg=seg0),
                    dict(lch=lch,
                         w_p=w_p,
                         w_n=w_n,
                         th_p=th_p,
                         th_n=th_n,
                         seg=seg1),
                ],
            )

        in_pins = get_bit_list(in_pin)
        wrapper_params = get_digital_wrapper_params(specs,
                                                    dut,
                                                    in_pins,
                                                    buf_params=buf_params)
        dut_in_pins = [get_in_buffer_pin_names(pin)[1] for pin in in_pins]

        mm_specs = self.specs.copy()
        mm_specs.pop('buf_config', None)
        mm_specs.pop('buf_params', None)
        mm_specs['in_pin'] = in_pin
        if 'start_pin' not in mm_specs:
            mm_specs['start_pin'] = dut_in_pins
        mm_specs['wrapper_params'] = wrapper_params
        mm = sim_db.make_mm(CombLogicTimingMM, mm_specs)

        return await mm.async_measure_performance(name, sim_dir, sim_db, dut)
Beispiel #9
0
    def init_search(
        self, sim_db: SimulationDB, dut: DesignInstance
    ) -> Tuple[TestbenchManager, Mapping[str, Any], Mapping[str, Mapping[
            str, Any]], Mapping[str, Any], bool, bool]:
        specs = self.specs
        assert specs['sigma_avt'] >= 0
        intv_params: Mapping[str, Any] = specs['intv_params']
        out_pins: Sequence[str] = specs.get(
            'out_pins', ['t_up', 't_down'])  # specified as ['up', 'down']
        early_clk: str = specs['early_clk']
        late_clk: str = specs['late_clk']
        reset_list: Sequence[Tuple[str,
                                   bool]] = specs.get('rst_pins',
                                                      [('RSTb', False)])
        invert_clk: bool = specs['invert_clk']
        clk_pol = not invert_clk

        clk_pins = [early_clk, late_clk]
        load_list = [
            dict(pin=out_pin, value='cload', type='cap')
            for out_pin in out_pins
        ]
        early_clk_pulse = dict(pin=early_clk,
                               tper='5/4*t_bit+t_delay',
                               tpw='3/4*t_bit',
                               trf='t_rf',
                               td='t_bit+t_bit/4',
                               pos=clk_pol)
        late_clk_pulse = dict(pin=late_clk,
                              tper='2*t_bit',
                              tpw='t_bit',
                              trf='t_rf',
                              td='t_bit',
                              pos=clk_pol)
        pulse_list = [early_clk_pulse, late_clk_pulse]
        digital_tran_tb_params = dict(
            pulse_list=pulse_list,
            reset_list=reset_list,
            load_list=load_list,
        )
        wrapper_params = get_digital_wrapper_params(specs, dut, clk_pins)
        tbm_specs, tb_params = setup_digital_tran(
            specs,
            dut,
            wrapper_params=wrapper_params,
            **digital_tran_tb_params)
        # TODO Add this back in
        # for v_offset in self.specs['strongarm_offset_params'][mos]:
        #     tbm.sim_params[v_offset] = 3 * self.specs['sigma_avt'] * (1 if pol == 'pos' else -1)

        tbm = cast(DigitalTranTB, sim_db.make_tbm(DigitalTranTB, tbm_specs))
        self.adj_dict = {'t_delay': intv_params}

        return tbm, tb_params, self.adj_dict, {}, False, True
Beispiel #10
0
 async def _measure_custom(self, name: str, sim_dir: Path, sim_db: SimulationDB,
                           dut: Optional[DesignInstance], meas_name: str, meas_cls: str,
                           meas_specs: Mapping[str, Any], ans: Dict[str, Any]) -> None:
     sim_env_name: str = self.specs['sim_env_name']
     mm_specs = dict(tbm_specs=self._tran_specs, fake=self.fake, sim_env_name=sim_env_name,
                     **meas_specs)
     mm = sim_db.make_mm(meas_cls, mm_specs)
     sim_id = f'custom_{meas_name}'
     mm_result = await sim_db.async_simulate_mm_obj(f'{name}_{sim_id}', sim_dir / sim_id,
                                                    dut, mm)
     for pin, timing_data in mm_result.data.items():
         cur_info = ans[pin]
         timing_list = cur_info.get('timing', None)
         if timing_list is None:
             cur_info['timing'] = timing_data
         else:
             timing_list.extend(timing_data)
    async def get_in_timing(self, name: str, sim_db: SimulationDB,
                            dut: Optional[DesignInstance], sim_dir: Path,
                            meas_mode: FlopMeasMode, fake: bool,
                            t_clk_rf: float, t_rf: float, ck_idx: int,
                            rf_idx: int, arr_shape: Tuple[int, ...],
                            t_clk_rf_first: bool,
                            timing_table: Dict[str, Any]) -> None:
        constraint_min_map: Mapping[Tuple[str, bool], float] = self.specs.get(
            'constraint_min_map', {})

        mm_specs = self.specs.copy()
        mm_specs.pop('constraint_min_map', None)
        mm_specs['meas_mode'] = meas_mode
        mm_specs['fake'] = fake
        cons_key = (meas_mode.input_mode_name, meas_mode.meas_setup)
        mm_specs['constraint_min'] = constraint_min_map.get(
            cons_key, float('-inf'))
        mm = sim_db.make_mm(FlopConstraintTimingMM, mm_specs)
        sim_params = mm.specs['tbm_specs']['sim_params']
        sim_params['t_clk_rf'] = t_clk_rf
        sim_params['t_rf'] = t_rf
        sim_params['c_load'] = mm_specs['c_load']
        mm.commit()

        state = self._get_state(meas_mode, t_clk_rf, t_rf,
                                mm_specs['tbm_specs']['sim_envs'])

        meas_result = await sim_db.async_simulate_mm_obj(
            f'{name}_{state}', sim_dir / state, dut, mm)

        meas_data = meas_result.data
        arr_sel = (ck_idx, rf_idx) if t_clk_rf_first else (rf_idx, ck_idx)
        for val_dict in meas_data.values():
            timing_info: Mapping[str, Any] = val_dict['timing_info']
            timing_val: float = val_dict['value']
            pin_data_list: Sequence[Tuple[str,
                                          str]] = timing_info['pin_data_list']
            ttype_str: str = timing_info['timing_type']

            for pin_name, data_name in pin_data_list:
                arr_table = _get_arr_table(timing_table, pin_name, ttype_str,
                                           timing_info)
                arr = arr_table.get(data_name, None)
                if arr is None:
                    arr_table[data_name] = arr = np.full(arr_shape, np.nan)
                arr[arr_sel] = timing_val
Beispiel #12
0
    def initialize(self, sim_db: SimulationDB,
                   dut: DesignInstance) -> Tuple[bool, MeasInfo]:
        specs = self.specs
        fake: bool = specs.get('fake', False)

        load_list = [dict(pin='ckout', type='cap', value='c_load')]
        pulse_list = [
            dict(pin='launch',
                 tper='2*t_bit',
                 tpw='t_bit',
                 trf='t_rf',
                 td='t_bit',
                 pos=True),
            dict(pin='measure',
                 tper='2*t_bit',
                 tpw='t_bit',
                 trf='t_rf',
                 td='2*t_bit',
                 pos=True)
        ]
        pin_values = dict(dcc_byp=0, clk_dcd=0)
        save_outputs = ['launch', 'measure', 'ckout']
        tbm_specs, tb_params = setup_digital_tran(specs,
                                                  dut,
                                                  pulse_list=pulse_list,
                                                  load_list=load_list,
                                                  pin_values=pin_values,
                                                  save_outputs=save_outputs)
        tbm = cast(DigitalTranTB, sim_db.make_tbm(DigitalTranTB, tbm_specs))

        if fake:
            td = np.full(tbm.sweep_shape[1:], 50.0e-12)
            trf = np.full(td.shape, 20.0e-12)
            result = _get_result_table(td, td, trf, trf)
            return True, MeasInfo('done', result)

        tbm.sim_params['t_sim'] = 't_rst+t_rst_rf+11*t_bit'
        self._tbm_info = tbm, tb_params
        return False, MeasInfo('sim', {})
Beispiel #13
0
    async def _measure_delay(self, name: str, sim_id: str, sim_dir: Path,
                             sim_db: SimulationDB,
                             dut: Optional[DesignInstance], pin_name: str,
                             related: str, sense_str: str, cond: Mapping[str,
                                                                         int],
                             timing_type_str: str, zero_delay: bool,
                             user_data: Optional[Mapping[str, Any]],
                             output_list: List[Dict[str, Any]]) -> None:
        specs = self.specs
        sim_env_name: str = specs['sim_env_name']
        delay_shape: Tuple[int, ...] = specs['delay_shape']

        sense = TimingSenseType[sense_str]
        if sense is TimingSenseType.non_unate:
            raise ValueError(
                'Must specify timing sense for output measurement')
        out_invert = (sense is TimingSenseType.negative_unate)

        ttype: TimingType = TimingType[timing_type_str]
        keys = []
        if ttype.is_rising:
            keys.append('cell_rise')
            keys.append('rise_transition')
        if ttype.is_falling:
            keys.append('cell_fall')
            keys.append('fall_transition')

        data = {}
        if user_data is not None:
            for name in keys:
                cur_data = user_data[name]
                if isinstance(cur_data, Mapping):
                    val = user_data[name][sim_env_name]
                else:
                    val = cur_data
                data[name] = np.broadcast_to(val, delay_shape)
        elif zero_delay:
            for name in keys:
                data[name] = np.zeros(delay_shape)
        elif self.fake:
            for name in keys:
                val = 50.0e-12 if name.startswith('cell') else 20.0e-12
                data[name] = np.full(delay_shape, val)
        else:
            cur_specs = self._delay_specs.copy()
            cur_specs['in_pin'] = related
            cur_specs['out_pin'] = pin_name
            cur_specs['out_invert'] = out_invert
            cur_specs['out_rise'] = ttype.is_rising
            cur_specs['out_fall'] = ttype.is_falling
            if cond:
                pin_values = cur_specs['tbm_specs']['pin_values'].copy()
                pin_values.update(cond)
                update_recursive(cur_specs, pin_values, 'tbm_specs',
                                 'pin_values')

            mm = sim_db.make_mm(CombLogicTimingMM, cur_specs)
            mm_result = await sim_db.async_simulate_mm_obj(
                f'{name}_{sim_id}', sim_dir / sim_id, dut, mm)
            delay_data = mm_result.data['timing_data'][pin_name]

            for name in keys:
                # NOTE: remove corners
                data[name] = delay_data[name][0, ...]

        ans = dict(
            related=related,
            timing_type=ttype.name,
            cond=build_timing_cond_expr(cond),
            sense=sense_str,
            data=data,
        )
        output_list.append(ans)
Beispiel #14
0
    async def async_measure_performance(
            self, name: str, sim_dir: Path, sim_db: SimulationDB,
            dut: Optional[DesignInstance]) -> Dict[str, Any]:
        specs = self.specs
        r_unit: float = specs['r_src']
        c_unit: float = specs['c_load']
        c_in: float = specs.get('c_in', 0)
        plot: bool = specs.get('plot', False)
        t_step_min: float = specs.get('t_step_min', 0.1e-12)

        td_mm = sim_db.make_mm(CombLogicTimingMM, self._td_specs)
        mm_output = await sim_db.async_simulate_mm_obj(f'{name}_td',
                                                       sim_dir / 'td', dut,
                                                       td_mm)
        mm_result = mm_output.data

        sim_envs = mm_result['sim_envs']
        sim_params = mm_result['sim_params']
        # NOTE: with ideal step input, the "delay resistance" is ln(2) times physical resistance.
        r_td = sim_params['r_src'] * np.log(2)
        c_load = sim_params['c_load']

        delay_data = mm_result['timing_data']['d']
        td_rise: np.ndarray = delay_data['cell_rise']
        td_fall: np.ndarray = delay_data['cell_fall']

        num = len(sim_envs)
        res_rise = np.empty(num)
        res_fall = np.empty(num)
        cs_rise = np.empty(num)
        cs_fall = np.empty(num)
        cd_rise = np.empty(num)
        cd_fall = np.empty(num)
        t_unit = 10 * t_step_min
        for idx in range(len(sim_envs)):
            rs = r_td[idx, ...]
            cl = c_load[idx, ...]
            tdr = td_rise[idx, ...]
            tdf = td_fall[idx, ...]
            self._fit_rc(idx, tdr, rs, c_in, cl, res_rise, cs_rise, cd_rise,
                         r_unit, c_unit, t_unit)
            self._fit_rc(idx, tdf, rs, c_in, cl, res_fall, cs_fall, cd_fall,
                         r_unit, c_unit, t_unit)

        if plot:
            # noinspection PyUnresolvedReferences
            from mpl_toolkits.mplot3d import Axes3D
            from matplotlib import pyplot as plt
            from matplotlib import cm
            if len(sim_envs) > 100:
                raise ValueError('Can only plot with num. sim_envs < 100')
            for idx in range(len(sim_envs)):
                rs = r_td[idx, ...]
                cl = c_load[idx, ...]
                tdr = td_rise[idx, ...]
                tdf = td_fall[idx, ...]

                rs_fine = np.logspace(np.log10(rs[0]),
                                      np.log10(rs[-1]),
                                      num=51)
                cl_fine = np.logspace(np.log10(cl[0]),
                                      np.log10(cl[-1]),
                                      num=51)
                tdr_calc = (rs_fine * (cl_fine + cs_rise[idx] + cd_rise[idx]) +
                            res_rise[idx] * (cd_rise[idx] + cl_fine))
                tdf_calc = (rs_fine * (cl_fine + cs_fall[idx] + cd_fall[idx]) +
                            res_fall[idx] * (cd_fall[idx] + cl_fine))

                for fig_idx, rf_str, td, td_calc in [
                    (idx * 100, 'rise', tdr, tdr_calc),
                    (idx * 100 + 1, 'fall', tdf, tdf_calc)
                ]:
                    fig = plt.figure(fig_idx)
                    ax = fig.add_subplot(111, projection='3d')
                    ax.set_title(f'{sim_envs[idx]}_{rf_str}')
                    ax.plot_surface(rs_fine,
                                    cl_fine,
                                    td_calc,
                                    rstride=1,
                                    cstride=1,
                                    cmap=cm.get_cmap('cubehelix'))
                    ax.scatter(rs.flatten(), cl.flatten(), td.flatten(), c='k')
            plt.show()

        return dict(
            sim_envs=sim_envs,
            r_p=(res_fall, res_rise),
            c_s=(cs_fall, cs_rise),
            c_d=(cd_fall, cd_rise),
        )
    def initialize(self, sim_db: SimulationDB, dut: DesignInstance) -> Tuple[bool, MeasInfo]:
        specs = self.specs
        in_pin: str = specs['in_pin']
        search_params = specs['search_params']
        buf_params: Optional[Mapping[str, Any]] = specs.get('buf_params', None)
        buf_config: Optional[Mapping[str, Any]] = specs.get('buf_config', None)
        fake: bool = specs.get('fake', False)
        load_list: Sequence[Mapping[str, Any]] = specs.get('load_list', [])

        if fake:
            return True, MeasInfo('done', dict(cap_rise=1.0e-12, cap_fall=1.0e-12,
                                               tr_ref=50.0e-12, tf_ref=50.0e-12,
                                               tr_adj=50.0e-12, tf_adj=50.0e-12))

        if buf_params is None and buf_config is None:
            raise ValueError('one of buf_params or buf_config must be specified.')

        if buf_params is None:
            lch: int = buf_config['lch']
            w_p: int = buf_config['w_p']
            w_n: int = buf_config['w_n']
            th_p: str = buf_config['th_p']
            th_n: str = buf_config['th_n']
            cinv_unit: float = buf_config['cinv_unit']
            cin_guess: float = buf_config['cin_guess']
            fanout_in: float = buf_config['fanout_in']
            fanout_buf: float = buf_config.get('fanout_buf', 4)

            seg1 = int(round(max(cin_guess / fanout_in / cinv_unit, 1.0)))
            seg0 = int(round(max(seg1 / fanout_buf, 1.0)))
            buf_params = dict(
                export_pins=True,
                inv_params=[
                    dict(lch=lch, w_p=w_p, w_n=w_n, th_p=th_p, th_n=th_n, seg=seg0),
                    dict(lch=lch, w_p=w_p, w_n=w_n, th_p=th_p, th_n=th_n, seg=seg1),
                ],
            )
            specs['buf_params'] = buf_params
            self.log(f'buf_params:\n{pprint.pformat(buf_params, width=100)}')
            search_params = dict(**search_params)
            search_params['guess'] = (cin_guess * 0.8, cin_guess * 1.2)

        # create testbench for measuring reference delay
        pulse_list = [dict(pin=in_pin, tper='2*t_bit', tpw='t_bit', trf='t_rf',
                           td='t_bit', pos=True)]
        self._wrapper_params = get_digital_wrapper_params(specs, dut, [in_pin])
        tbm_specs, tb_params = setup_digital_tran(specs, dut, wrapper_params=self._wrapper_params,
                                                  pulse_list=pulse_list, load_list=load_list)
        buf_mid, buf_out = get_in_buffer_pin_names(in_pin)
        tbm_specs['save_outputs'] = [buf_mid, buf_out]
        # remove input pin from reset list
        reset_list: Sequence[Tuple[str, bool]] = tbm_specs.get('reset_list', [])
        new_reset_list = [ele for ele in reset_list if ele[0] != in_pin]
        tbm_specs['reset_list'] = new_reset_list

        tbm = cast(DigitalTranTB, sim_db.make_tbm(DigitalTranTB, tbm_specs))
        if tbm.swp_info:
            self.error('Parameter sweep is not supported.')
        if tbm.num_sim_envs != 1:
            self.error('Corner sweep is not supported.')
        tbm.sim_params['t_sim'] = f'{tbm.t_rst_end_expr}+3*t_bit'

        self._tbm_info = tbm, tb_params

        # create DelayMatch
        mm_tbm_specs = {k: v for k, v in tbm.specs.items()
                        if k not in {'pwr_domain', 'sup_values', 'dut_pins', 'pin_values',
                                     'pulse_load', 'reset_list', 'load_list', 'diff_list'}}
        gnd_name, pwr_name = DigitalTranTB.get_pin_supplies(in_pin, tbm_specs['pwr_domain'])
        sup_values: Mapping[str, Union[float, Mapping[str, float]]] = tbm_specs['sup_values']
        gnd_val = sup_values[gnd_name]
        pwr_val = sup_values[pwr_name]
        pwr_tup = ('VSS', 'VDD')
        mm_tbm_specs['pwr_domain'] = {}
        mm_tbm_specs['sup_values'] = dict(VSS=gnd_val, VDD=pwr_val)
        mm_tbm_specs['pin_values'] = {}

        thres_lo: float = mm_tbm_specs['thres_lo']
        thres_hi: float = mm_tbm_specs['thres_hi']
        t_start_expr = f't_rst+t_bit+(t_rst_rf-t_rf/2)/{thres_hi - thres_lo:.2f}'
        mm_specs = dict(
            adj_name='c_load',
            adj_sign=True,
            adj_params=dict(in_name='mid', out_name='out', t_start=t_start_expr),
            ref_delay=0,
            use_dut=False,
            search_params=search_params,
            tbm_specs=mm_tbm_specs,
            wrapper_params=dict(
                lib='bag3_digital',
                cell='inv_chain',
                params=buf_params,
                pins=['in', 'out', 'mid', 'VDD', 'VSS'],
                pwr_domain={'in': pwr_tup, 'out': pwr_tup, 'mid': pwr_tup},
            ),
            pulse_list=[dict(pin='in', tper='2*t_bit', tpw='t_bit', trf='t_rf',
                             td='t_bit', pos=True)],
            load_list=[dict(pin='out', type='cap', value='c_load')],
        )
        mm_specs.update(search_params)
        self._mm = sim_db.make_mm(DelayMatch, mm_specs)

        return False, MeasInfo('init', {})
    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
    async def get_out_timing(self, name: str, sim_db: SimulationDB,
                             dut: Optional[DesignInstance], sim_dir: Path,
                             tbm_cls: Type[FlopTimingBase],
                             meas_mode: FlopMeasMode, fake: bool,
                             timing_table: Dict[str, Any]) -> None:
        specs = self.specs
        search_params: Mapping[str, Any] = specs['search_params']
        flop_params: Mapping[str, Any] = specs['flop_params']
        out_swp_info: Sequence[Any] = specs['out_swp_info']
        sim_env_name: str = specs.get('sim_env_name', '')

        tbm_specs, tb_params = setup_digital_tran(specs,
                                                  dut,
                                                  meas_mode=meas_mode,
                                                  flop_params=flop_params,
                                                  sim_env_name=sim_env_name)
        tbm = cast(FlopTimingBase, sim_db.make_tbm(tbm_cls, tbm_specs))
        c_load = specs.get('c_load', None)
        if c_load is not None:
            tbm.sim_params['c_load'] = c_load
        if tbm.num_sim_envs != 1:
            raise ValueError(
                'Cannot have corner sweep in flop characterization.')
        sim_id = f'out_delay_{meas_mode.name.lower()}_{tbm.sim_envs[0]}'

        tbm.set_swp_info(out_swp_info)
        output_map = tbm.get_output_map(True)
        ttype = TimingType.rising_edge if meas_mode.is_pos_edge_clk else TimingType.falling_edge
        ttype_str = ttype.name
        if fake:
            sim_data = None
        else:
            sim_params = tbm.sim_params
            t_clk_per = sim_params['t_clk_per']
            t_rf = sim_params['t_rf'] / (tbm.thres_hi - tbm.thres_lo)
            max_margin: float = search_params.get('max_margin', t_clk_per / 4)
            # NOTE: max_timing_value makes sure PWL width is always non-negative
            max_timing_value = max_margin + t_rf
            for var in tbm.timing_variables:
                sim_params[var] = max_timing_value

            sim_results = await sim_db.async_simulate_tbm_obj(
                sim_id,
                sim_dir / sim_id,
                dut,
                tbm,
                tb_params,
                tb_name=f'{name}_{sim_id}')

            sim_data = sim_results.data

        # fill in results
        data_shape = tbm.sweep_shape[1:]
        for timing_info, edge_out_list in output_map.values():
            offset = timing_info.get('offset', 0)
            for edge, out_list in edge_out_list:
                for out_pin in out_list:
                    arr_table = _get_arr_table(timing_table, out_pin,
                                               ttype_str, timing_info)
                    td, trf = _get_out_data(tbm, sim_data, out_pin, edge,
                                            data_shape)
                    if edge is EdgeType.RISE:
                        arr_table['cell_rise'] = td + offset
                        arr_table['rise_transition'] = trf
                    else:
                        arr_table['cell_fall'] = td + offset
                        arr_table['fall_transition'] = trf
Beispiel #18
0
    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