def func_info_to_func_and_kwargs(func_info): ''' There are several ways to specify commands in the YML file. This parses them into function name and arguments It can be a list where first element is a function and second is a dict of kwargs. It can be a dict where key is function and value is dict of kwargs. ''' if isinstance(func_info, str): func_name = func_info kwargs = dict() elif isinstance(func_info, list): message( 'Deprecation warning: spefifying a step as a list is going to go. Use dicts.' ) func_name = func_info[0] if len(func_info) == 1: kwargs = dict() elif len(func_info) == 2: kwargs = func_info[1] else: raise TypeError( 'Function not specified correctly as a list (needs two elements): {}' .format(func_info)) elif isinstance(func_info, dict): if len(func_info.keys()) != 1: raise TypeError( 'Function not specified correctly as a dictionary (needs one key): {}' .format(func_info)) for k, v in func_info.items(): func_name = k kwargs = v else: raise TypeError( 'Function not specified correctly. Need str, list, dict: {}'. format(func_info)) return func_name, kwargs
def resolve_ymlspec(ymlspec=None, technology=None, category='dataprep'): ''' Find the yml file that describes the process. There are several options for inputs # Option 1: file path is specified directly # Option 2: search within a specified technology This also sets the active_technology stored in the module ''' if ymlspec is not None and os.path.isfile(os.path.realpath(ymlspec)): # Option 1: file path is specified directly ymlfile = ymlspec if technology is not None: set_active_technology(technology) tech_obj = active_technology() if technology is None: message('Using the last used technology: {}'.format(tech_obj.name)) else: # Option 2: search within a specified technology if technology is None: raise ValueError( 'When specifying a relative dataprep file, you must also provide a technology.' ) tech_obj = Technology.technology_by_name(technology) set_active_technology(technology) if ymlspec is None: # default dataprep test ymlspec = 'default' # find path to tech if not ymlspec.endswith('.yml'): ymlspec += '.yml' ymlfile = tech_obj.eff_path(os.path.join(category, ymlspec)) if not os.path.isfile(ymlfile): raise FileNotFoundError( 'Could not resolve YAML specification: {}'.format(ymlspec)) return ymlfile
def check_floorplan(cell, fp_safe=50): ''' Checks for floorplan. If you didn't make one, this makes one. ''' if cell.shapes(lys.FLOORPLAN).is_empty(): message('Warning: No floorplan found. Making one, but you should do this yourself') fp_box = cell.dbbox() # this assumes that, if FLOORPLAN is present, that DRC has verified it forms the extent fp_box.enlarge(fp_safe, fp_safe) cell.shapes(lys.FLOORPLAN).insert(fp_box)
def _main(layout, ymlfile, tech_obj=None): with open(ymlfile) as fx: step_list = yaml.load(fx, Loader=yaml.FullLoader) reload_lys(tech_obj, dataprep=True) assert_valid_dataprep_steps(step_list) for func_info in step_list: func_name, kwargs = func_info_to_func_and_kwargs(func_info) message('lymask doing {}'.format(func_name)) func = all_dpfunc_dict[func_name] for TOP_ind in layout.each_top_cell(): # call it func(layout.cell(TOP_ind), **kwargs) return layout
def parse_message(msg): ''' Takes a msg read from the socket and does something with it. Careful of name conflict with lygadgets. The returned payload is not the same as what is returned from the called function: It is prepended with a status token and encoded (as a str) to be sent back over the socket ''' return_val = None tokens = msg.split(' ') try: # if msg == 'kill': # message('Stopping server -- remote shutdown') # pya.Application.exit(pya.Application.instance()) if tokens == ['reload', 'view']: main = pya.Application.instance().main_window() main.cm_reload() elif tokens[0] == 'ping': message_loud('I heard something') elif tokens[0] == 'load': filename = os.path.realpath(tokens[1]) message(filename) if len(tokens) > 2: mode = int(tokens[2]) quiet_load_layout(filename, mode) else: quiet_load_layout(filename) elif tokens[0] == 'cellview': cellname = tokens[1] main = pya.Application.instance().main_window() view = main.current_view() cv = view.active_cellview() cv.set_cell_name(cellname) # show all layers of hierarchy # fit to screen else: message('Received {}'.format(msg)) except Exception: # Convert the stack trace to string to send to client payload = 'ERR ' + repr(traceback.format_exc( )) # repr makes it so multi-line strings go as one string else: # Tell the client that it worked payload = 'ACK ' + str(return_val) return payload
def batch_drc_main(infile, ymlspec=None, technology=None, outfile=None): # covers everything that is not GUI if outfile is None: outfile = infile[:-4] + '.lyrdb' # Load it layout = pya.Layout() layout.read(infile) lys.active_layout = layout ymlfile = resolve_ymlspec(ymlspec, technology, category='drc') # this also sets the technology tech_obj = active_technology() # Process it rdb = _drc_main(layout, ymlfile=ymlfile, tech_obj=tech_obj) # Write it rdb.save(outfile) # Brief report message('DRC violations:', rdb.num_items()) message('Full report:', outfile)
def _drc_main(layout, ymlfile, tech_obj=None): with open(ymlfile) as fx: step_list = yaml.load(fx, Loader=yaml.FullLoader) if func_info_to_func_and_kwargs(step_list[0])[0] != 'make_rdbcells': step_list.insert(0, ['make_rdbcells']) reload_lys(tech_obj, dataprep=True) # assert_valid_drc_steps(step_list) rdb = pya.ReportDatabase('DRC: {}'.format(os.path.basename(ymlfile))) rdb.description = 'DRC: {}'.format(os.path.basename(ymlfile)) for func_info in step_list: func_name, kwargs = func_info_to_func_and_kwargs(func_info) message('lymask doing {}'.format(func_name)) func = all_drcfunc_dict[func_name] for TOP_ind in layout.each_top_cell(): func(layout.cell(TOP_ind), rdb, **kwargs) return rdb
def __init__(self, port=lyipc.PORT, parent=None): pya.QTcpServer.__init__(self, parent) localhost = pya.QHostAddress() self.listen(localhost, port) self.newConnection(self.new_connection) message('Server initialized with {}, {}'.format(localhost, port))
def run_xor_phidl(file1, file2, tolerance=1, verbose=False, hash_geom=True): import phidl.geometry as pg from lytest.phidl_oas import import_oas TOPS = [] for fn in [file1, file2]: TOPS.append(import_oas(fn)) TOP1, TOP2 = TOPS XOR = xor_polygons_phidl(TOP1, TOP2, hash_geom=True) if len(XOR.flatten().get_polygons()) > 0: raise GeometryDifference( "Differences found between layouts {} and {}".format(file1, file2)) # if you have failed to import klayout.db or pya, it's going to go slower but it can be done with phidl if pya is None: message('Detected no klayout standalone. We will use phidl.') message('You should "pip install klayout"') run_xor = run_xor_phidl if __name__ == "__main__": ''' For command line argument usage, run ``python kdb_xor.py --help`` If there is a difference found, this script will return a non-zero exit code. Typical usage from a bash script:: python kdb_xor.py a.gds b.gds || failed=true # alternatively: if !(python kdb_xor.py a.gds b.gds); then failed=true fi