def get_dut_conns(self, dut_pins: Iterable[str], src_pins: Set[str], pin_values: Mapping[str, int]) -> Mapping[str, str]: pwr_domain: Mapping[str, Tuple[str, str]] = self.specs['pwr_domain'] ans = {} for pin_name in dut_pins: pin_val: Optional[int] = pin_values.get(pin_name, None) basename, bus_range = parse_cdba_name(pin_name) if bus_range is None: # scalar pins if pin_name in src_pins or pin_val is None: ans[pin_name] = pin_name else: ans[pin_name] = self.get_pin_supplies( pin_name, pwr_domain)[pin_val] else: # bus pins if pin_val is None: # no bias values specified ans[pin_name] = pin_name else: nlen = len(bus_range) bin_str = bin(pin_val)[2:].zfill(nlen) ans[pin_name] = self._bin_str_to_net( basename, bus_range, bin_str, pwr_domain, src_pins) return ans
def get_dut_conns( self, dut_pins: Iterable[str], src_pins: Set[str], pin_values: Mapping[str, int]) -> Tuple[Dict[str, str], List[str]]: pwr_domain: Mapping[str, Tuple[str, str]] = self.specs['pwr_domain'] ans = {} no_conn = [] for pin_name in dut_pins: pin_val: Optional[int] = pin_values.get(pin_name, None) basename, bus_range = parse_cdba_name(pin_name) if bus_range is None: # scalar pins if pin_name in src_pins or pin_val is None: ans[pin_name] = pin_name else: ans[pin_name] = self.get_pin_supplies( pin_name, pwr_domain)[pin_val] if pin_name not in src_pins and pin_val is None: no_conn.append(pin_name) else: # bus pins if pin_val is None: # no bias values specified ans[pin_name] = pin_name no_conn.append(pin_name) # [no_conn.append(f"pin_name<{idx}>") for idx in range(max(bus_range.start, bus_range.stop))] else: nlen = len(bus_range) bin_str = bin(pin_val)[2:].zfill(nlen) ans[pin_name] = self._bin_str_to_net( basename, bus_range, bin_str, pwr_domain, src_pins) return ans, no_conn
def get_pin_supplies( cls, pin_name: str, pwr_domain: Mapping[str, Tuple[str, str]]) -> Tuple[str, str]: ans = pwr_domain.get(pin_name, None) if ans is None: basename, _ = parse_cdba_name(pin_name) return pwr_domain[basename] return ans
def get_recovery_removal_name(cls, pin: str) -> Tuple[str, str]: basename, bus_range = parse_cdba_name(pin) if bus_range is None: v1 = f't_recovery_{basename}_' v2 = f't_removal_{basename}_' else: v1 = f't_recovery_{basename}_{bus_range[0]}' v2 = f't_removal_{basename}_{bus_range[0]}' return v1, v2
def get_setup_hold_name(cls, pin: str) -> Tuple[str, str]: basename, bus_range = parse_cdba_name(pin) if bus_range is None: var_setup = f't_setup_{basename}_' var_hold = f't_hold_{basename}_' else: var_setup = f't_setup_{basename}_{bus_range[0]}' var_hold = f't_hold_{basename}_{bus_range[0]}' return var_setup, var_hold
def get_pin_supplies( cls, pin_name: str, pwr_domain: Mapping[str, Tuple[str, str]]) -> Tuple[str, str]: ans = pwr_domain.get(pin_name, None) if ans is None: # check if this is a r_src pin pin_base = cls.get_r_src_pin_base(pin_name) if pin_base: return pwr_domain[pin_base] # check if this is a bus pin, and pwr_domain is specified for the whole bus basename = parse_cdba_name(pin_name)[0] return pwr_domain[basename] return ans
def _get_tbm_specs(sim_envs: Sequence[str], env_params: Mapping[str, Any], dut_pins: Sequence[str], tbit: float, trf: float, cload: float, nbits: int, rtol: float, atol: float) -> Dict[str, Any]: tsim = tbit * (2 * nbits + 2) + tbit / 2 pulse_list = [ dict(pin='a_in', tper=tbit, tpw=tbit / 2, trf=trf, td=tbit / 2) ] for i in range(nbits): pulse_list.append( dict(pin=f'sn<{i}>', tper=2 * tsim, tpw=tsim, trf=trf, td=(2 * i + 2) * tbit + tbit / 4, pos=False)) pulse_list.append( dict(pin=f'sp<{i}>', tper=2 * tsim, tpw=tsim, trf=trf, td=(2 * i + 2) * tbit + tbit / 4, pos=True)) pin_values = {} load_list = [dict(pin='intout', type='cap', value=cload)] pwr_domains = { parse_cdba_name(pin)[0]: ('VSS', 'VDD') for pin in dut_pins } sim_params = dict( t_sim=tsim, t_rst=0, t_rst_rf=trf, ) sup_values = dict(VSS=0, VDD=env_params['vdd']) return dict(sim_params=sim_params, dut_pins=dut_pins, pulse_list=pulse_list, load_list=load_list, pwr_domain=pwr_domains, sup_values=sup_values, pin_values=pin_values, reset_list=[], diff_list=[], rtol=rtol, atol=atol, sim_envs=sim_envs, env_params=env_params, save_outputs=['a_in', 'a_in_buf', 'intout', 'b_in'])
def _get_pin_info_list(src_list: Sequence[Mapping[str, Any]], defaults: Mapping[str, Any], pwr_domain: Dict[str, Tuple[str, str]], reset_table: Optional[Dict[str, bool]] = None, in_cap_table: Optional[Dict[str, float]] = None, cap_guess: float = 1.0e-15 ) -> List[Dict[str, Any]]: pin_list = [] empty_dict = {} default_dict = {k: v for k, v in defaults.items()} for pin_info in src_list: pin_name: str = pin_info['name'] reset_val: Optional[int] = pin_info.get('reset_val', None) basename, bus_range = parse_cdba_name(pin_name) if bus_range is None: # scalar pin if 'basename' in pin_info: raise ValueError('Scalar pins cannot have basename entry defined.') cur_info = default_dict.copy() cur_info.update(pin_info) cur_info.pop('hide', None) in_cap_guess = cur_info.pop('cap_guess', cap_guess) if in_cap_table is not None: in_cap_table[pin_name] = in_cap_guess # record power domain pwr_domain[pin_name] = (cur_info['gnd_pin'], cur_info['pwr_pin']) if reset_val is not None: if reset_table is None: raise ValueError('reset_table is not given but reset_val is defined.') if pin_name in reset_table: raise ValueError(f'pin {pin_name} is already in reset_table.') reset_table[pin_name] = (reset_val == 1) if not pin_info.get('hide', False): pin_list.append(cur_info) else: # bus pin values: Optional[List[Dict[str, Any]]] = pin_info.get('values', None) bus_defaults: Dict[str, Any] = pin_info.get('defaults', empty_dict) cur_defaults = default_dict.copy() cur_defaults.update(bus_defaults) if 'reset_val' not in cur_defaults: cur_defaults['reset_val'] = None num_bits = len(bus_range) # NOTE: make dictionary copies, so we can add cap info to them later if values is None: values = [cur_defaults.copy() for _ in range(num_bits)] elif len(values) != num_bits: raise ValueError(f'values list of bus {pin_name} length mismatch') else: values = [] for val_ in values: val_ = val_.copy() val_.update(cur_defaults) values.append(val_) # record power domain and reset values for bus_idx, bit_info in zip(bus_range, values): pwr_str: str = _get('pwr_pin', bit_info, cur_defaults) gnd_str: str = _get('gnd_pin', bit_info, cur_defaults) reset_val: Optional[int] = _get('reset_val', bit_info, cur_defaults) in_cap_guess = bit_info.pop('cap_guess', cap_guess) bit_name = get_bus_bit_name(basename, bus_idx, cdba=True) pwr_domain[bit_name] = (gnd_str, pwr_str) if reset_val is not None: if reset_table is None: raise ValueError('reset_table is not given but reset_val is defined.') if bit_name in reset_table: raise ValueError(f'pin {bit_name} is already in reset_table.') reset_table[bit_name] = (reset_val == 1) if in_cap_table is not None: in_cap_table[bit_name] = in_cap_guess if not pin_info.get('hide', False): pin_list.append(dict(name=pin_name, basename=basename, bus_range=bus_range, values=values)) return pin_list
def design(self, dut_lib: str, dut_cell: str, in_file_list: Sequence[Sequence[str]], clk_file_list: Sequence[Sequence[str]], load_list: Optional[Sequence[Sequence[str]]], vbias_list: Optional[Sequence[Sequence[str]]], other_list: Optional[List[Mapping[str, Any]]], dut_conns: Dict[str, str], dut_params: Optional[Param], no_conns: Sequence[str], src_list: Sequence[Mapping[str, Any]]) -> None: """Design the testbench. The elements of parameter lists are either (pos_term, param) or (pos_term, neg_term, param), where pos_term/neg_term are the positive/negative terminals of the voltage sources or capacitors. The negative terminal defaults to VSS if not specified. for ``load_list`` and ``vbias_list``, if None is given (the default), then the default load/bias voltages will be used (the ones shown in schematic template). If an empty list is given, then they'll be removed entirely. Parameters ---------- dut_lib : str DUT library name dut_cell : str DUT cell name in_file_list : Sequence[Sequence[str]] List of PWL input stimuli files clk_file_list : Sequence[Sequence[str]] List of PWL clk stimuli files load_list : Optional[Sequence[Sequence[str]]] List of ideal capacitor loads other_list : Optional[Sequence[Sequence[str]]] List of other devices for tb vbias_list : Optional[Sequence[Sequence[str]]] List of voltage biases dut_conns : Dict[str, str] DUT connection dictionary dut_params: Optional[Param] Replace the DUT statically if empty, otherwise call design with dut_params. no_conns: List[str] Connects the content of this list to noConn. src_list : Sequence[Mapping[str, Any]] list of sources and loads. """ if no_conns: len_no_conn = 0 for pin in no_conns: basename, bus_range = parse_cdba_name(pin) if bus_range is None: len_no_conn += 1 else: len_no_conn += max(bus_range.start, bus_range.stop) + 1 self.rename_instance('XNC', f'XNC<{len_no_conn - 1}:0>', [('noConn', ','.join(no_conns))]) else: self.delete_instance('XNC') if vbias_list is None: vbias_list = [('VDD', 'vdd')] # combine src_list and load_list src_load_list = list(src_list) if load_list: for cap_info in load_list: if len(cap_info) == 2: pos_term, val = cap_info neg_term = 'VSS' elif len(cap_info) == 3: pos_term, neg_term, val = cap_info else: raise ValueError(f'Cannot parse cap element: {cap_info}') src_load_list.append( dict(type='cap', lib='analogLib', value=val, conns=dict(PLUS=pos_term, MINUS=neg_term))) # setup DUT dut_static = dut_params is None self.replace_instance_master('XDUT', dut_lib, dut_cell, static=dut_static, keep_connections=True) if not dut_static: self.instances['XDUT'].design(**dut_params) self.reconnect_instance('XDUT', ((k, v) for k, v in dut_conns.items())) # setup PWL files def get_path_str(fname: str) -> str: return json.dumps(str(Path(fname).resolve())) self._array_and_set_params('VIN', in_file_list, 'fileName', get_path_str) self._array_and_set_params('VCLK', clk_file_list, 'fileName', get_path_str) # setup voltage biases self._array_and_set_params('VSUP', vbias_list, 'vdc', None) # setup sources and loads self.design_sources_and_loads(src_load_list, default_name='CLOAD') if other_list: name_list = [] element_list = [] for other_dev in other_list: name_list.append(other_dev['name']) element_list.append((other_dev['name'], other_dev['conn'], other_dev['params'])) self.array_instance('XSW', inst_name_list=name_list) for name, conns, val_dict in element_list: inst = self.instances[name] for k, v in val_dict.items(): inst.set_param(k, v) self.reconnect_instance(name, conns.items()) else: self.remove_instance('XSW')