def test_PMOS(library, test_ckt): assert any(x.name == 'PMOS' for x in library) with set_context(test_ckt.elements): with pytest.raises(Exception): inst = Instance( name='M1', model='PMOS', pins={'D': 'NET10', 'G': 'NET12', 'S': 'NET13'}, generator='MOS') with pytest.raises(Exception): inst = Instance( name='X1', model='PMOS', pins={'D': 'NET10', 'G': 'NET12', 'S': 'NET13', 'B': 'VSS'}, generator='MOS') inst = Instance( name='M1', model='PMOS', pins={'D': 'NET10', 'G': 'NET12', 'S': 'NET13', 'B': 'VSS'}, generator='MOS') assert inst.name == 'M1' assert inst.model == 'PMOS' assert inst.pins == {'D': 'NET10', 'G': 'NET12', 'S': 'NET13', 'B': 'VSS'} assert list(inst.parameters.keys()) == ['W', 'L', 'NFIN'] assert inst.parameters['W'] == '0' assert inst.parameters['L'] == '0' assert inst.parameters['NFIN'] == '1' with set_context(test_ckt.elements): inst = Instance( name='M1', model='PMOS', pins={'D': 'NET10', 'G': 'NET12', 'S': 'NET13', 'B': 'VSS'}, parameters={'NFIN': 2}, generator='MOS') assert inst.parameters['NFIN'] == '2'
def test_instance_init_parameters(circuit): with set_context(circuit.elements): M1 = Instance(name='M1', model='testmos', pins={ 'D': 'NET01', 'G': 'NET02', 'S': 'NET03', 'B': 'NET04' }, parameters={'PARAM1': 'NF*4'}, generator='MOS') assert M1.parameters == {'PARAM1': 'NF*4', 'PARAM2': "2"} with set_context(circuit.elements): M1 = Instance(name='M1', model='testmos', pins={ 'D': 'NET01', 'G': 'NET02', 'S': 'NET03', 'B': 'NET04' }, parameters={ 'PARAM1': '12', 'PARAM2': '13' }, generator='MOS') assert M1.parameters == {'PARAM1': "12", 'PARAM2': "13"}
def remove_dummies(library, dummy_hiers, top): for dh in dummy_hiers: if dh == top: logger.debug("Cant delete top hierarchy {top}") return ckt = library.find(dh) assert ckt, f"No subckt with name {dh} found" with set_context(library): logger.info(f"Flattening dummy hierarchy {ckt.name}") for other_ckt in library: if isinstance(other_ckt, SubCircuit) and not other_ckt.name == ckt.name: replace = {} for inst in other_ckt.elements: if inst.model == ckt.name: logger.debug( f"Removing instance {inst} with instance {ckt.elements[0].model}" ) replace[inst.name] = ckt.elements[0] # @Parijat, is there a better way to modify? with set_context(other_ckt.elements): for x, y in replace.items(): ele = other_ckt.get_element(x) assert ele pins = {} for p, v in y.pins.items(): pins[p] = ele.pins[v] y.parameters.update({ k: v for k, v in ele.parameters.items() if k in y.parameters }) logger.debug( f"new instance parameters: {y.parameters}") _prefix = library.find(y.model).prefix if not _prefix: _prefix = "M" # default value, used in testing other_ckt.elements.append( Instance( name=ele.name.replace("X", _prefix), model=y.model, pins=pins, parameters=y.parameters, generator=y.generator, )) logger.info( f"updating {other_ckt.name} element {other_ckt.elements[-1]}" ) other_ckt.elements.remove(ele) all_subckt = [ module.name for module in library if isinstance(module, SubCircuit) ] library.remove(ckt) logger.info(f"Removing hierarchy {dh} from {all_subckt}") all_subckt_updated = [ module.name for module in library if isinstance(module, SubCircuit) ] assert library.find(dh) == None, f"{all_subckt_updated}"
def test_subckt_definition(library, test_ckt): with set_context(library): subckt = SubCircuit(name='SUBCKT', pins=['PIN1', 'PIN2'], parameters={ 'PARAM1': 1, 'PARAM2': '1E-3', 'PARAM3': '0.1F', 'PARAM4': 'HELLO' }) library.append(subckt) with set_context(test_ckt.elements): with pytest.raises(Exception): inst = Instance(name='X1', model='subckt', generator='Dummy') with pytest.raises(Exception): inst = Instance(name='X1', model='subckt', pins={'PIN1': 'NET10'}, generator='Dummy') inst = Instance(name='X1', model='subckt', pins={ 'PIN1': 'NET10', 'PIN2': 'NET12' }, generator='Dummy') assert inst.name == 'X1' assert inst.model == 'SUBCKT' assert inst.pins == {'PIN1': 'NET10', 'PIN2': 'NET12'} assert list( inst.parameters.keys()) == ['PARAM1', 'PARAM2', 'PARAM3', 'PARAM4'] assert inst.parameters['PARAM1'] == '1' assert inst.parameters['PARAM2'] == '1E-3' assert inst.parameters['PARAM3'] == '0.1F' assert inst.parameters['PARAM4'] == 'HELLO' with set_context(test_ckt.elements): with pytest.raises(Exception): inst = subckt(name='X1', model='subckt', pins={ 'PIN1': 'NET10', 'PIN2': 'NET12' }, parameters={'garbage': ''}, generator='Dummy') inst = Instance(name='X1', model='subckt', pins={ 'PIN1': 'NET10', 'PIN2': 'NET12' }, parameters={ 'param1': '2', 'param3': '1e-16' }, generator='Dummy') assert inst.parameters['PARAM1'] == '2' assert inst.parameters['PARAM3'] == '1E-16'
def test_subckt_instantiation(library, test_ckt): with set_context(library): subckt = SubCircuit(name='SUBCKT', pins=['PIN1', 'PIN2'], parameters={ 'PARAM1': 1, 'PARAM2': 1e-3, 'PARAM3': 1E-16, 'PARAM4': "HELLO" }) library.append(subckt) with set_context(subckt.elements): X1 = Instance(name='X1', model='TwoTerminalDevice', pins={ 'A': 'NET1', 'B': 'NET2' }, generator='Dummy') X2 = Instance(name='X2', model='TwoTerminalDevice', pins={ 'A': 'NET2', 'B': 'NET3' }, generator='Dummy') subckt.elements.append(X1) subckt.elements.append(X2) assert subckt.elements == [X1, X2] assert subckt.elements[0] == X1 assert subckt.elements[1] == X2 assert subckt.get_element('x1') == X1 assert subckt.nets == ['NET1', 'NET2', 'NET3'] with set_context(test_ckt.elements): with pytest.raises(Exception): inst = Instance(name='X1', model='subckt', generator='Dummy') with pytest.raises(Exception): inst = Instance(name='X1', model='subckt', pins={'PIN1': 'NET10'}, generator='Dummy') inst = Instance(name='X1', model='subckt', pins={ 'PIN1': 'NET10', 'PIN2': 'NET12' }, generator='Dummy') assert inst.name == 'X1' assert inst.model == 'SUBCKT' assert inst.pins == {'PIN1': 'NET10', 'PIN2': 'NET12'} assert inst.parameters == { 'PARAM1': '1', 'PARAM2': '0.001', 'PARAM3': '1E-16', 'PARAM4': 'HELLO' }
def test_ConstraintDB_json(db): with set_context(db): db.append( constraint.Order(direction='left_to_right', instances=['M1', 'M2'])) db.append( constraint.Order(direction='left_to_right', instances=['M1', 'M3'])) fp = pathlib.Path(__file__).parent / 'const.json' fp.write_text(db.json()) with set_context(db.parent): newdb = constraint.ConstraintDB.parse_file(fp) assert db == newdb
def resolve_parameters(self, name, param): subckt = self.lib.find(name.upper()) assert subckt, f"No subckt found with name: {name}" assert isinstance(subckt, SubCircuit), f"{subckt.name} is not a subcircuit" logger.debug( f"Resolving subckt {name} param {subckt.parameters} with {param} ") if name.upper() in self.multi_param_instantiation: # Second time instantiation of this circuit with same parameters if all(v == param[p] for p, v in subckt.parameters.items() if p in param): self.multi_param_instantiation.append(name.upper()) logger.debug( f"Same parameters found {param} {subckt.parameters}") return name.upper() # Second time instantiation of this circuit with different parameters new_name, updated_param = self._find_new_inst_name(subckt, param) if new_name == subckt.name or self.lib.find(new_name): logger.debug( f"Second similar instance found of module {new_name},{self.multi_param_instantiation}" ) else: logger.debug( f"New instance found of {name}, assigning name {new_name}") self.multi_param_instantiation.append(new_name) with set_context(self.lib): subckt_new = SubCircuit( name=new_name, pins=subckt.pins, parameters=updated_param, constraints=subckt.constraints, ) assert (self.lib.find(new_name) is None), f"Redefining subckt with name {new_name}" self.lib.append(subckt_new) with set_context(subckt_new.elements): for ele in subckt.elements: subckt_new.elements.append(ele.copy()) self._update_instances(subckt_new) return new_name else: self.multi_param_instantiation.append(name.upper()) logger.debug( f"New subckt definition found {subckt.name} {subckt.parameters}" ) for p in subckt.parameters.keys(): if p in param: subckt.parameters[p] = param[p] self._update_instances(subckt) return name.upper()
def process_symmnet(self): logger.debug(f"input const {self.iconst}") replace_const = list() new_symmblock_const = list() for const in self.iconst: if isinstance(const, constraint.SymmetricNets): # if not getattr(const, 'pins1', None): # TODO: constr with pin information should be handled separately logger.debug(f"adding pins to user symmnet constraint {const}") pairs, s1, s2 = symmnet_device_pairs( self.G, const.net1.upper(), const.net2.upper(), self.user_constrained_list, None, True) assert s1, f"no connections found to net {const.net1}, fix user const" assert s2, f"no connections found to net {const.net2}, fix user const" with set_context(self.iconst): replace_const.append(( const, constraint.SymmetricNets( direction=const.direction, net1=const.net1.upper(), net2=const.net2.upper(), pins1=s1, pins2=s2, ), )) pairsj = list() for key, value in pairs.items(): if key in s1: continue if key != value and { key, value } not in self.user_constrained_list: self.user_constrained_list.append({key, value}) pairsj.append([key, value]) elif key not in self.user_constrained_list: self.user_constrained_list.append(key) pairsj.append([key]) if len(pairsj) > 0: symmBlock = constraint.SymmetricBlocks(direction="V", pairs=pairsj) new_symmblock_const.append(symmBlock) with set_context(self.iconst): for k, v in replace_const: self.iconst.remove(k) self.iconst.append(v) self.user_constrained_list.append(v.net1) self.user_constrained_list.append(v.net2) for symb in new_symmblock_const: self.iconst.append(symb)
def test_add_symmetry_const(): name = f'ckt_{get_test_id()}' netlist = ota_six(name) constraints = [ {"constraint": "IsDigital", "isTrue": True} ] example = build_example(name, netlist, constraints) ckt_library = compiler_input(example, name, pdk_path, config_path) ckt = ckt_library.find(name) with set_context(ckt.constraints): x = constraint.SymmetricBlocks(direction="V", pairs=[["MN4", "MN3"]]) const_pairs = {"MN4": "MN3"} # skip dictionary element with pytest.raises(KeyError): add_or_revert_const(const_pairs, ckt.constraints, list()) assert len(ckt.constraints) == 1 const_pairs = [["MN4", "MN3"]] add_or_revert_const(const_pairs, ckt.constraints, list()) assert len(ckt.constraints) == 2 assert ckt.constraints[1] == x const_pairs = [["MN4", "MN5"]] # Skip unequal size add_or_revert_const(const_pairs, ckt.constraints, list()) assert len(ckt.constraints) == 2 const_pairs = [["VIN", "VIP"]] # Skip net add_or_revert_const(const_pairs, ckt.constraints, list()) assert len(ckt.constraints) == 2 clean_data(name)
def filter_symnet_const(self, pairs: list): for key, value in pairs: if self.pre_fiter(key, value): continue if not self.G.nodes[key].get("instance"): if key != value: pairs, s1, s2 = symmnet_device_pairs( self.G, key, value, self.written_symmblocks, self.skip_const) if pairs: self.written_symmblocks.append({key, value}) with set_context(self.iconst): symmnet = constraint.SymmetricNets(direction="V", net1=key, net2=value, pins1=s1, pins2=s2) self.iconst.append(symmnet) logger.debug(f"adding symmetric net const: {symmnet}") else: logger.debug( f"Skip symmetry: large fanout nets {key} {value} {pairs}" ) # TODO Need update in placer to simplify this else: logger.debug(f"skipping self symmetric nets {key} {value}")
def test_model_instantiation(circuit): with set_context(circuit.elements): M1 = Instance(name='m1', model='testmos', pins={ 'd': 'net01', 'G': 'Net02', 's': 'NET03', 'B': 'NeT04' }, parameters={ 'PARAM1': 'nf*4', 'param2': '2.0' }, generator='MOS') M2 = Instance(name='m2', model='testmos', pins={ 'd': 'net03', 'G': 'Net02', 's': 'NET01', 'B': 'NeT04' }, parameters={ 'PARAM1': '2.0', 'param2': 'nf*4' }, generator='MOS') assert M1 != M2 assert M1.name != M2.name assert M1.pins != M2.pins assert M1.parameters != M2.parameters assert M1.model == M2.model
def test_AlignInOrder_input_sanitation(): with set_context(db): x = constraint.AlignInOrder(instances=['M1', 'M2'], line='top') x = constraint.AlignInOrder(instances=['M1', 'M2', 'M3'], line='top') with pytest.raises(Exception): x = constraint.AlignInOrder(instances=['M1', 'M2', 'M3'], line='garbage')
def propagate_const_top_to_bottom(self, top_name, traversed): top = self.lib.find(top_name) all_subckt = { inst.model for inst in top.elements if isinstance(self.lib.find(inst.model), SubCircuit) } all_subckt = all_subckt - traversed if not all_subckt: return for const in top.constraints: global_const = [ constraint.IsDigital, constraint.AutoConstraint, constraint.AutoGroupCaps, constraint.FixSourceDrain, constraint.KeepDummyHierarchies, constraint.MergeSeriesDevices, constraint.MergeParallelDevices, constraint.IdentifyArray, constraint.DoNotUseLib ] if any(isinstance(const, x) for x in global_const): for child in all_subckt: child_const = self.lib.find(child).constraints if const not in child_const and const.propagate: with set_context(child_const): child_const.append(const) traversed.update(all_subckt) for child in all_subckt: self.propagate_const_top_to_bottom(child, traversed)
def test_constraint(library): assert len(library.find("DP_PMOS_B").constraints) == 3 dp_const = library.find("DP_PMOS_B").constraints with set_context(dp_const): x = constraint.SymmetricBlocks(direction="V", pairs=[["M0", "M1"]]) assert x in dp_const assert dp_const[0].constraint == "symmetric_blocks" assert dp_const[0].pairs == [["M0", "M1"]]
def test_Order_input_sanitation(db): with set_context(db): x = constraint.Order(direction='left_to_right', instances=['M1', 'M2']) x = constraint.Order(direction='left_to_right', instances=['M1', 'M2', 'M3']) with pytest.raises(Exception): x = constraint.Order(direction='lefta_to_rightb', instances=['M1', 'M2', 'M3'])
def test_instance_name(circuit): with set_context(circuit.elements): with pytest.raises(Exception): M1 = Instance(model='testmos') with pytest.raises(Exception): M1 = Instance(name='M1') with pytest.raises(Exception): M1 = Instance(name='M1', model='testmos')
def library(): library = Library() with set_context(library): model = Model(name='TwoTerminalDevice', pins=['A', 'B'], parameters={'MYPARAMETER': '3'}) library.append(model) return library
def mock_circuit(): library = Library() n = library.find('nmos') assert n is not None with types.set_context(library): subckt = SubCircuit( name = 'high_speed_comparator', pins = ['clk', 'vcc', 'vin', 'vip', 'von', 'vop', 'vss']) dummy_sub = SubCircuit( name = 'dummy', pins = ['D', 'G', 'S', 'B']) library.append(subckt) library.append(dummy_sub) for instance in [ 'MN0', 'MN1', 'MN2', 'MN3', 'MN4', 'X_CMC_S_NMOS_B_I1_M12_M14', 'MP5', 'MP6', 'MP7', 'MP8', 'MP9', 'C2', 'C5', 'C1', 'X_CMC_S_NMOS_B_I1_M6_M7', 'C8', 'C9', 'MP10', 'MP11', 'MN13', 'MP12', 'MN14', 'X_CMC_PMOS_MP10_MP7', 'X_CMC_PMOS_MP8_MP9', 'X_DP_MN1_MN2', 'X_CCN_MN3_MN4', 'X_CCP_MP5_MP6', 'X_INV_N_MP11_MN13', 'X_INV_P_MP12_MN14']: with types.set_context(subckt.elements): if instance.startswith('M'): subckt.elements.append( Instance( name=instance, model='nmos', generator='', pins={'D': 'NET10', 'G': 'NET12', 'S': 'NET11', 'B': 'NET13'}) ) elif instance.startswith('C'): subckt.elements.append( Instance( name=instance, model='cap', generator='', pins={'PLUS': 'NET10', 'MINUS': 'NET12'}) ) else: subckt.elements.append( Instance( name=instance, model='dummy', generator='', pins={'D': 'NET10', 'G': 'NET12', 'S': 'NET11', 'B': 'NET13'}) ) return subckt
def test_AspectRatio_input_sanitation(db): with set_context(db): x = constraint.AspectRatio(subcircuit="amplifier", ratio_low=0.1, ratio_high=0.5) with pytest.raises(Exception): x = constraint.AspectRatio(subcircuit="amplifier", ratio_low=0.6, ratio_high=0.5)
def test_derived_model_case_insensitivity(testmos): with set_context(testmos.parent): DerivedMOS = Model(name='DerivedMOS', base='testmos', parameters={'param1': 'nf*4'}) assert DerivedMOS.name == 'DERIVEDMOS' assert DerivedMOS.base == 'TESTMOS' assert DerivedMOS.pins == ['D', 'G', 'S', 'B'] assert DerivedMOS.parameters == {'PARAM1': 'NF*4', 'PARAM2': '2'}
def test_model_xyce(testmos): assert testmos.xyce( ) == '* .MODEL TESTMOS ElementaryDevice(D, G, S, B) PARAM1={1.0} PARAM2={2}' with set_context(testmos.parent): newmos = Model(name='newmos', base='TESTMOS', parameters={'param3': '3'}) assert newmos.xyce( ) == '.MODEL NEWMOS TESTMOS PARAM1={1.0} PARAM2={2} PARAM3={3}'
def test_library_registration(library): with set_context(library): test = Model( name='TEST', prefix = 'M', pins=['D', 'G', 'S', 'B'], parameters={'W': 0, 'L': 0, 'NFIN': 1}) library.append(test) assert any(x.name == 'TEST' for x in library) assert test.parent == library assert test.name == 'TEST'
def test_ConstraintDB_nonincremental_revert(db): ''' While it is possible to revert() back to a certain checkpoint() by name, needing to unroll multiple checkpoints can indicate suboptimal compiler design ''' with set_context(db): constraint1 = constraint.Order(direction='left_to_right', instances=['M1', 'M2']) db.append(constraint1) idx = db.checkpoint() assert str(db.parent._checker.label(constraint1)) in str( db.parent._checker._solver) with set_context(db): constraint2 = constraint.Order(direction='left_to_right', instances=['M1', 'M3']) db.append(constraint2) db.checkpoint() assert str(db.parent._checker.label(constraint1)) in str( db.parent._checker._solver) assert str(db.parent._checker.label(constraint2)) in str( db.parent._checker._solver) with set_context(db): constraint3 = constraint.Order(direction='left_to_right', instances=['M2', 'M3']) db.append(constraint3) db.checkpoint() assert str(db.parent._checker.label(constraint1)) in str( db.parent._checker._solver) assert str(db.parent._checker.label(constraint2)) in str( db.parent._checker._solver) assert str(db.parent._checker.label(constraint3)) in str( db.parent._checker._solver) db.revert(idx) assert len(db) == 1 assert len(db._commits) == 0 assert str(db.parent._checker.label(constraint1)) in str( db.parent._checker._solver) assert str(db.parent._checker.label(constraint2)) not in str( db.parent._checker._solver) assert str(db.parent._checker.label(constraint3)) not in str( db.parent._checker._solver)
def testmos(library): with set_context(library): model = Model(name='TESTMOS', pins=['D', 'G', 'S', 'B'], parameters={ 'PARAM1': '1.0', 'PARAM2': '2' }) library.append(model) return model
def test_ConstraintDB_parent_relationship(db): with set_context(db): db.append( constraint.Order(direction='left_to_right', instances=['M1', 'M2'])) db.append( constraint.Order(direction='left_to_right', instances=['M1', 'M3'])) for const in db: assert const.parent == db
def test_AlignInOrder_smt_checking(db): with set_context(db): db.append( constraint.AlignInOrder(instances=['M1', 'M2', 'M3'], direction='horizontal')) db.append( constraint.AlignInOrder(instances=['M4', 'M5'], line='bottom')) with pytest.raises(SolutionNotFoundError): db.append( constraint.AlignInOrder(instances=['M3', 'M2'], line='bottom'))
def test_AspectRatio_smt_checking(db): with set_context(db): db.append( constraint.AspectRatio(subcircuit="amplifier", ratio_low=0.1, ratio_high=0.5)) with pytest.raises(SolutionNotFoundError): db.append( constraint.AspectRatio(subcircuit="amplifier", ratio_low=0.6, ratio_high=1.0))
def test_group_block_hsc(results_file, mock_circuit): name = pathlib.Path(pathlib.Path(results_file.stem).stem).stem constraint_file = pathlib.Path(__file__).parent.parent / 'files' / 'test_results' / (name + '.const.json') assert constraint_file.is_file() tmp_dir = pathlib.Path(__file__).parent.parent / 'tmp' tmp_dir.mkdir(exist_ok=True) tmp_file = tmp_dir / (name + '.pnr.const.json') with types.set_context(mock_circuit): constraints = constraint.ConstraintDB.parse_file(constraint_file) with types.set_context(mock_circuit.constraints): mock_circuit.constraints.extend(constraints) with open(tmp_file, 'w') as outfile: json.dump(PnRConstraintWriter().map_valid_const(mock_circuit.constraints), outfile, indent=4) with open(tmp_file, "r") as const_fp: gen_const = json.load(const_fp)["constraints"] gen_const.sort(key=lambda item: item.get("const_name")) with open(results_file, "r") as const_fp: gold_const = json.load(const_fp)["constraints"] gold_const.sort(key=lambda item: item.get("const_name")) assert gold_const == gen_const
def library(): library = List[Model]() with set_context(library): model = Model(name='TESTMOS', pins=['D', 'G', 'S', 'B'], parameters={ 'PARAM1': '1.0', 'PARAM2': '2' }) library.append(model) return library
def test_new_model(library): with set_context(library): with pytest.raises(Exception): MyDevice = Model() with pytest.raises(Exception): MyDevice = Model(name='MyDevice') MyDevice = Model(name='MyDevice', pins=['D', 'S'], parameters={ 'PARAM1': '1.0', 'PARAM2': '2' })