############################################## # This is the "Back Drill File Generator" utility (aka BackDrillGen) # # Input: BackDrillGen takes one configuration file as input for describing the work to do: # # This input file gets executed as python commands and must follow python syntax # (e.g. all parameter names given must start at column #1 in configuration file) # The default configuration file name is "BackDrillGen.cfg" # An alternate input file may be given with command line parameter <-c other_config_file_name> # # The Configuration file may include # # - "input_trace_file_name": # The name of a Mentor Trace file to read as input # e.g. # input_trace_file_name = "traces.traces_512" # This configuration parameter is required # # - "net_name_pattern_list": # This is a list where each entry describes a net name pattern to be matched. # The net names that match this pattern will be analysed and included in the output files. # Each entry in the net_name_pattern_list must itself also be a list made of the desired field segments to be matched. # Each field segment can be a string (made of characters and numbers) or one of two special wildcard characters. # "#" specifies that at least one digit must appear at this position. # "&" specifies that either "_CMP" or "_DIR" has to appear at this position # (_P and _N can easily be added if needed). # By defaul the pattern must be matched exactly, piece-wise, with no extra character. # A variable "pattern_match_strict" may be overriden to False to enable a looser pattern match and allow Extremitya characters # This configuration is optional but the default is an empty list (i.e. not very useful) # e.g. to find all differential pairs of signals GTH_FO_CH_*_IN, i.e. FEX Data from Backplane to Fanout # net_name_pattern_list = ( ( "GTH_FO_CH_", "#", "_IN", "&" ), ) # # - "named_regions_dict" # This provides entries for a dictionary of Named Rectangular Regions on the board. # Each Region is specified by its lower-left (X_min, Y_min) and upper-right (X_max, Y_max) coordinates # For each Named Region the coordinate information must be given in the form of a list # ( X_min, Y_min, X_max, Y_max ) # The coordinates are expected in units of millimeters # As many Named Regions as necessary may be specified. # e.g. # named_regions_dict [ "ATCA_Zone2" ] = ( 275.00, 75.00, 276.50, 220.00 ) # This configuration is optional and the default is to not specify any Named Region # The program will, for each wire within each seleved net, # show in the logfile which region is matched # show a "WARNING" if one end of the other is not within a defined region # Falling outside of a named region prevents a wire end become a candidate for further rule processing # # - "drill_rules_dict" # This provides entries for a translation dictionary describing # every Mentor physical layer number found in the traces file. # # For each entry two pieces of information are given. # The first is an arbitrary name to associate with the Mentor physical layer number. # This arbitraty name could for example describe our stackup layer name. # This name is only used for cosmetic purposes in screen output and the log file. # The second is a dictionary that provides the information necessary (i.e. the "rules") # to direct the program on what to do for # all net extremities found on that layer. # all transisitions between that layer and another layer # Note: rules for layer transition only need to be specfied for the smaller # to the bigger physical Mentor layer number, as the trace data is sorted in that way. # # The rule names are arbitrary strings and these names will be used # in the screen output and log file # and in drill_output_files_dict below to specify the name of the associated drill file # # Filling entries in the dictionaries takes two steps. # The first step is to give the stackup layer name and create an empty (sub-)dicitonary. # e.g. if physical layer 2 is used for Stackup layer L3 # drill_rules_dict ["2"] = ( "L3 Diff Pair HS", {} ) # The second step or set of steps to finish filling that entry is to specify # as many rule names as necessary for (at least) all cases which will be encountered. # e.g. if we want to say that an extremity found on layer 2 will be drilled from the bottom: # drill_rules_dict ["2"][idx_rules_drill_dict][key_net_extremity] = "Drill From Bot & Keep L3-L1" # Note the use of the cosmetic variable "idx_rules_drill_dict" to aim at the dictionary. # Note the use of the cosmetic variable "key_net_extremity" to specify a rule for the net extremity. # e.g. if we want to say that a transtion from layer 9 to 13 will be drilled from the top: # drill_rules_dict ["9"][idx_rules_drill_dict]["13"] = "Drill From Top & Keep L14-L22" # As many "rules" need to be specified as cases will be encountered in the trace file. # A missing rule will be flagged as an "ERROR". # For cases where the intention is to do nothing (no drilling), use the variable "Keyword_No_Drill", e.g. # drill_rules_dict ["1"][idx_rules_drill_dict]["12"] = Keyword_No_Drill # # As many physical layer entries as necessary may be specified. # This information is required for any layer that the program will find. # If an entry is completly missing, python will crash with a fairly descriptive "KeyError" referring to drill_rules_dict # # # - "drill_output_files_dict" # This provides entries for a translation dictionary that must include information # for EVERY Drill "rule" which has been given an otherwise arbitrary name in drill_rules_dict above. # # For each entry two pieces of information are given. # The first is the name of the drill file to be created. # The second is an optional name for a Mentor Layer to receive a circle at the corresponding drill location. # e.g. # drill_output_files_dict[ "Drill From Bot & Keep L3-L1" ] = ( "Drill_FromBot_Keep_L3toL1.drl" , "SHEET_DIELECTRIC_4" ) # # In addition, two more optional entries may be added in the dictionary using special keywords: # A dictionary entry for key_mentor_check provides the name of the file with circle commands for visual check in Mentor. # e.g. # drill_output_files_dict[ key_mentor_check ] = ( "mentor_check_backdrill.txt", "" ) # A dictionary entry for key_drill_any provides the name of a Mentor layer to receive a circle at ANY back drill location # e.g. # drill_output_files_dict[ key_drill_any ] = ( "", "SHEET_DIELECTRIC_12" ) # Note that for these two special entries, only one of the two fields contains relevant information, the other is unused. # # Output files: # # One logfile BackDrillGen.log in the current directory # # activity messages are printed to the screen and copied to the logfile # # Several (optional) output files: # - one Drill File for each Rule specified # - one file to create circles at drill locations for visual Mentor check # # ############################################## # Version 0.1 24-Oct-2016: Initial version - practice reading and parsing traces file # Version 0.2 31-Oct-2016: Add more precise pattern matching # Version 0.3 01-Nov-2016: Add output to make circles for a Mentor check of all points found # Version 0.4 02-Nov-2016: Look for matches among wires start and end points to find whole path # Version 0.5 03-Nov-2016: Build list of transitions and dump it out # Version 0.6 08-Nov-2016: (re)Add file with circles for a Mentor check of all transitions found # and start drill files just just with place-holder content # Version 1.0 09-Nov-2016: Create output(s) in Drill File format # Version 1.1 14-Nov-2016: Clean up and add drill count for each file # Version 1.2 16-Nov-2016: Show location of wire end points being flagged outside of named regions # Version 1.3 18-Nov-2016: Add summary table of count of candidate points by region and layer transition to help with rule writing # Version 1.4 18-Nov-2016: Change Inclusion algotithm: a location must now be within a named region to become a candidate ############################################## app_version = "1.4" app_title = "BackDrillGen" log_file_name = "BackDrillGen.log" ############################################## import string import time import sys # import os ############################################## # Initialize default values for the name of the configuration file ############################################# # default configuration file name # this name may be overridden on the command line config_file_name = "BackDrillGen.cfg" ############################################## # Initialize default values for the configuration options # The options below may be overwriten in the configuration file ############################################# # The Mentor Trace file to read as input # This parameter must be provided in the configuration file input_trace_file_name = "" # The list of net names that will be analysed and included in output files net_name_pattern_list = ( ) # The suffixes to match for differential pairs # note: would still work if we add _P and _N lst_suffix_diff = ( "_DIR", "_CMP" ) # A switch to specify if net names have to exactly match the pattern requirements # when set to false, a looser definition is apply where the pattern only needs to be present pattern_match_strict = True # A switch to check parsing of pattern names debug_patterns = False # The dictionary of Named Board Rectangular Regions named_regions_dict = {} # The translation dictionary to go from Mentor physical layer to our stackup layer name # and provide rules for layer transitions drill_rules_dict = {} # The dictionary with the names of Drill files and Mentor layers to create circles for visual check drill_output_files_dict = {} # drill file header and trailer content drill_file_header = "G90\nM71\nT1\n" # this means Absolute Mode + Metric Code + use Tool #1 drill_file_trailer = "M30\n" # this means End of program # scaling ratio and line format for X and Y coordinates, from mm to the desired drill file units drill_mm_to_XYcoord_scale = 1000 # this goes from mm to micometers drill_coord_line_format = "X%06dY%06d\n" # this forces 6 digits each, with leading zeroes. ############################################## # cosmetic names increase code readability :-) ############################################## # used in parsing traces file chr_comment_delim = "#" str_net_line_start = "# NET" chr_net_delim = "'" str_last_line = "ST " # a long string of spaces used in padding str_40_spaces = " " str_40_dashes = "--------------------------------------" # wild cards used in parsing patterns chr_wildcard_num = "#" chr_wildcard_diff = "&" # index for array elements in named_regions_dict idx_region_Xmin = 0 idx_region_Ymin = 1 idx_region_Xmax = 2 idx_region_Ymax = 3 # net trace elements keywords len_trcpath_keyword = 3 # keywords are all 3 char long str_trcpath_wir = "WIR" # point to point str_trcpath_inc = "INC" # str_trcpath_ant = "ANT" # Antenna str_trcpath_gui = "GUI" # Guide (SLI) str_trcpath_seg = "SEG" # segment # from those, pick the set of allowed net trace elements keywords new_wire_keyword = ( str_trcpath_wir, ) # i.e. we allow just WIR # new_wire_keyword = ( str_trcpath_wir, str_trcpath_inc, str_trcpath_ant, str_trcpath_wui ) # from work with "reorder_traces" # new_wire_keyword = ( str_trcpath_wir, str_trcpath_inc ) # from what exists in Hub # order of fields found in a trace file "WIR" record # e.g. WIR 3 23278000 18055000 23340000 18080000 idx_wire_keyword = 0 idx_wire_tot_seg = 1 idx_wire_begX = 2 idx_wire_begY = 3 idx_wire_endX = 4 idx_wire_endY = 5 # order of fields found in a trace file "SEG" record # e.g. SEG 23278000 18080000 1 60000 # SEG 23340000 18080000 1 via_0mm60 # SEG 23340000 18080000 13 0 idx_segm_keyword = 0 idx_segm_X = 1 idx_segm_Y = 2 idx_segm_layer = 3 idx_segm_var = 4 # convert tens of nanometers to millimeters mentor_decanano_to_mm = 100000.0 # index in array "wire_sort_lst" which is used to sort and find matches for wire start(begin) and end points idx_sort_beg_coord = 0 # itself holding [coord_X,coord_Y] for start point idx_sort_end_coord = 1 # itself holding [coord_X,coord_Y] for end point idx_sort_beg_region = 2 idx_sort_end_region = 3 idx_sort_beg_layer = 4 idx_sort_end_layer = 5 idx_sort_beg_match = 6 # itself holding [match_code,wire_num] for start point idx_sort_end_match = 7 # itself holding [match_code,wire_num] for end point idx_coord_X = 0 # for X coordinate above idx_coord_Y = 1 # for Y coordinate above idx_sort_match_code = 0 # for match_code above idx_sort_wire_num = 1 # for wire_num above # codes for above to describe start and end point matching other wire sort_code_match_none = 0 sort_code_match_beg = 1 sort_code_match_end = 2 # index in array named "candidate" to record all net ends and wire transitions idx_cand_net_name = 0 idx_cand_type = 1 idx_cand_coord = 2 # itself holding [coord_X,coord_Y] idx_cand_region_name = 3 idx_cand_layer_smaller = 4 idx_cand_layer_bigger = 5 idx_cand_wire_from = 6 idx_cand_wire_to = 7 # for extremities there are no smaller/bigger and no to/from so we record less info idx_cand_layer = 4 idx_cand_wire = 5 # codes for candidate type above code_cand_extremity = 0 code_cand_same_layer = 1 # note: should not happen, but check for sanity code_cand_change_layer = 2 # the names of those codes for screen/log purpose str_code_cand_type = ( "Extremity", "No Change", # never happens but check "New Layer" ) # index in array recorded as a value in extr_count_dict or trans_count_dict idx_count_instance = 0 idx_missing_rule_flag = 1 str_rule_missing = "** No Rule Given **" # index for entries in "drill_rules_dict" where each entry is a list idx_rules_cosm_name = 0 idx_rules_drill_dict = 1 # keyword to use for a key in drill_rules_dict[][idx_rules_drill_dict] # to specify the Drill Rule for a Net extremity on that layer key_net_extremity = "NetEnd" # keyword to use for a value in drill_rules_dict[][idx_rules_drill_dict] # to explicitely specify that there will be no drilling for that case Keyword_No_Drill = "NoDrill" # index for the lists which are the values in "drill_output_files_dict" idx_output_file_name = 0 idx_output_mentor_layer = 1 # keyword to use for a key in drill_output_files_dict # to specify the file name that will specify circles a drill points for a visual check with Mentor key_mentor_check = "MentorCheck" # keyword to use for a key in drill_output_files_dict # to specify the Mentor Layer that will receive a circle for all drill points key_drill_any = "AnyDrill" ################################################################### ################################################################### # function we call in case of exception def show_exc_info ( logfile = None, stop_on_except = 0 ) : #---------------------------- exception_info = sys.exc_info() # retrieve exception info exception_type = exception_info[0] # exception type exception_value = exception_info[1] # exception value exception_at_line = exception_info[2].tb_lineno # traceback item print ' *** exception_type :', exception_type print ' *** exception_value :', exception_value print ' *** exception_at_line :', exception_at_line if ( logfile != None ): logfile.flush ( ) logfile.write ( ' *** exception_type :' + str(exception_type) + '\n' ) logfile.flush ( ) logfile.write ( ' *** exception_value :' + str(exception_value) + '\n' ) logfile.flush ( ) logfile.write ( ' *** exception_at_line :' + str(exception_at_line) + '\n' ) logfile.flush ( ) # del statement is executed left to right # We need to drop reference to traceback # This must be done explicitely (as below) when directly inside except clause # but it would be done implictely when returning from a function like here del [ exception_at_line, exception_value, exception_type, exception_info ] if stop_on_except : raise # stop and get a trace ################################################################### # function to print message to both screen and logfile def print_message ( status = "", app_logfile = None ) : #--------------------- print status if ( app_logfile != None ) : app_logfile.write ( "%s\n" % status ) ################################################################### #say hello #=========== print ' --------------------------' print ' %s V%s' % ( app_title, app_version ) print ' --------------------------' #open a logfile #============== app_logfile = open ( log_file_name, 'w' ) current_time = time.asctime ( time.localtime( time.time() ) ) app_logfile.write ( '\n\n Starting %s \n' % app_title ) app_logfile.write ( ' V%s -- %s\n' % ( app_version, current_time ) ) app_logfile.write ( '\n' ) # read and parse command line arguments #======================================= command_line_argument = sys.argv[1:] #print command_line_argument cmdarg_tot = len ( command_line_argument ) cmdarg_num = 0 while ( cmdarg_num < cmdarg_tot ) : # check for known argument switch and that there will always be a filename after the switch if ( ( command_line_argument[cmdarg_num] not in ( "-c", ) ) or ( cmdarg_num+1 >= cmdarg_tot ) ) : print_message ( "\nCommand line error at argument <%s>" % command_line_argument[cmdarg_num], app_logfile ) print "Argument Usage: [-c input_resource_filename] \n" sys.exit( 1 ) # alternate input configuration file name elif ( command_line_argument[cmdarg_num] == "-c" ) : config_file_name = command_line_argument[cmdarg_num+1] cmdarg_num = cmdarg_num + 2 # ingest the config file and display config parameters #===================================================== try: print_message ( "\nReading (i.e. executing) Configuration File <%s>" % config_file_name, app_logfile ) # execute that file execfile ( config_file_name ) # report on what was learned #--------------------------- # Name of Trace File if ( input_trace_file_name != "" ) : print_message ( "\nTrace File to be parsed : <%s>" % input_trace_file_name , app_logfile ) else : print_message ( "\nName of Trace File not found, aborting" , app_logfile ) sys.exit( 1 ) # Dump Net Name Patterns status = "Net Name Patterns to process are :" for net_name in net_name_pattern_list : status = "%s\n %s" % ( status, net_name ) print_message ( "%s" % status , app_logfile ) # Dump names of Named Regions max_region_name_length = 0 # will come handy for formatting messages if len ( named_regions_dict ) == 0 : print_message ( "No Named Board Region was defined " , app_logfile ) else : status = "Named Board Regions were specfied for :" for region in sorted ( named_regions_dict.iteritems(), key= lambda x: x[0] ) : region_name = region[0] status = "%s\n ( %6.2f, %6.2f ) - ( %6.2f, %6.2f ) = <%s>" \ % ( status, named_regions_dict[ region_name ] [ idx_region_Xmin ], named_regions_dict[ region_name ] [ idx_region_Ymin ], named_regions_dict[ region_name ] [ idx_region_Xmax ], named_regions_dict[ region_name ] [ idx_region_Ymax ], region_name ) region_name_length = len ( region_name ) if ( region_name_length > max_region_name_length ) : max_region_name_length = region_name_length print_message ( status , app_logfile ) # Dump Layer Rules information max_layer_name_length = 0 # will come handy for formatting messages max_rule_name_length = 0 # will come handy for formatting messages if len ( drill_rules_dict ) == 0 : print_message ( "No Rules and Stackup Layer Name was defined " , app_logfile ) else : status = "Stackup Layer Names and Rules were specfied for Physical Layer:" for drill_rules_entry in sorted ( drill_rules_dict.iteritems(), key=lambda x:"%2s" % x[0] ) : layer_phys_name = drill_rules_entry[0] layer_cosm_name = drill_rules_dict[layer_phys_name][idx_rules_cosm_name] status = "%s\n Layer %2s = <%s>" % ( status, layer_phys_name, layer_cosm_name ) layer_name_length = len ( layer_cosm_name ) if ( layer_name_length > max_layer_name_length ) : max_layer_name_length = layer_name_length for rule_entry in sorted ( drill_rules_dict[layer_phys_name][idx_rules_drill_dict].iteritems(), key=lambda x:"%2s" % x[0], reverse=True ) : rule_phys_layer = rule_entry[0] rule_cosm_name = drill_rules_dict[layer_phys_name][idx_rules_drill_dict][rule_phys_layer] status = "%s\n %6s : Rule Name <%s>" % ( status, rule_phys_layer, rule_cosm_name ) rule_name_length = len ( rule_cosm_name ) if ( rule_name_length > max_rule_name_length ) : max_rule_name_length = rule_name_length if ( rule_cosm_name == Keyword_No_Drill ) : status = "%s\n No Drill Output will be generated" % status elif drill_output_files_dict.has_key ( rule_cosm_name ) : drill_file_name = drill_output_files_dict[rule_cosm_name][idx_output_file_name] status = "%s\n Written to file <%s>" % ( status, drill_file_name ) mentor_layer_name = drill_output_files_dict[rule_cosm_name][idx_output_mentor_layer] status = "%s\n and Mentor Layer <%s>" % ( status, mentor_layer_name ) else : status = "%s\n ERROR: Output Information missing for this rule" % status print_message ( status , app_logfile ) # Dump Drill File information max_drill_file_name_length = 0 # will come handy for formatting messages max_mentor_layer_name_length = 0 # will come handy for formatting messages if len ( drill_output_files_dict ) == 0 : print_message ( 'No Rules Output Information was defined in "drill_output_files_dict"' , app_logfile ) else : print_message ( "Rules Output Information :" , app_logfile ) for drill_output_entry in sorted ( drill_output_files_dict.iteritems(), key=lambda x:"%2s" % x[0] ) : rule_cosm_name = drill_output_entry[0] print_message ( " Rule <%s> :" % rule_cosm_name , app_logfile ) drill_file_name = drill_output_files_dict[rule_cosm_name][idx_output_file_name] mentor_layer_name = drill_output_files_dict[rule_cosm_name][idx_output_mentor_layer] drill_file_name_length = len ( drill_file_name ) if ( drill_file_name_length > max_drill_file_name_length ) : max_drill_file_name_length = drill_file_name_length mentor_layer_name_length = len ( mentor_layer_name ) if ( mentor_layer_name_length > max_mentor_layer_name_length ) : max_mentor_layer_name_length = mentor_layer_name_length if ( rule_cosm_name == key_mentor_check ) : print_message ( " <%s> will contain the circles for Mentor Visual Check" % drill_file_name , app_logfile ) elif ( rule_cosm_name == key_drill_any ) : print_message ( " A Circle will be written to Mentor Layer <%s> for any Drill location" % mentor_layer_name , app_logfile ) else : print_message ( " Drill Location will be written to <%s>" % drill_file_name , app_logfile ) print_message ( " A Circle will be written to Mentor Layer <%s>" % mentor_layer_name , app_logfile ) except: print_message ( "Error Reading Config File" , app_logfile ) show_exc_info ( app_logfile, stop_on_except = 1 ) # read and parse trace file #=========================== print_message ( "\nReading Trace File <%s>" % input_trace_file_name, app_logfile ) input_trace_file = open ( input_trace_file_name, "r" ) # skip the header stuff up to the first NET comment line_num = 1 input_trace_line = input_trace_file.readline () [:-1] while ( input_trace_line[:len(str_net_line_start)] != str_net_line_start ) : line_num += 1 input_trace_line = input_trace_file.readline () [:-1] # dropping the end of line character # Now keep reading all the nets, select some and remember them in a dictionary filtered_net_dict = {} # clear dictionary found_end_of_traces = False # prepare flag for last record # repeat for every net until the last record has been found and flagged while not found_end_of_traces : # we enter here each time with a # NET statement cur_net_comment = input_trace_line # the net name in the NET comment will be the tag for the dictionary entry for this net # We can extract the net name loc_net_start = string.find ( cur_net_comment, chr_net_delim ) loc_net_end = string.find ( cur_net_comment[loc_net_start+1:], chr_net_delim ) + loc_net_start+1 if ( ( loc_net_start > 0 ) and ( loc_net_end > loc_net_start) ) : # now we have the net name cur_net_name = cur_net_comment [ loc_net_start+1 : loc_net_end ] #print_message ( " INFO: found net <%s> at line #%d" % ( cur_net_name, line_num ) , app_logfile ) else : cur_net_name = "" print_message ( " ERROR: no net name found at line #%d" % line_num , app_logfile ) print_message ( " .....: %s " % input_trace_line , app_logfile ) print_message ( " Aborting" , app_logfile ) sys.exit( 1 ) # Determine if we are going to record this net #--------------------------------------------- # Only record this net if it matches one of the filter requirements if ( len ( net_name_pattern_list ) == 0 ) : # special case : no pattern means all nets record_this_net = True # otherwise see if this name matches any of our target pattern else : record_this_net = False for net_name_pattern_entry in net_name_pattern_list : loc_parse_net_name = 0 pattern_match_failed = False # initialize before looking for match failure for net_name_pattern_field in net_name_pattern_entry : if ( net_name_pattern_field == chr_wildcard_num ) : # allow any number of digits if not cur_net_name[loc_parse_net_name].isdigit() : # require at least one pattern_match_failed = True if debug_patterns : print_message ( "DEBUG: <%s> failed first digit test" % cur_net_name[loc_parse_net_name:] , app_logfile ) # ; sys.exit( 1 ) else : if debug_patterns : print_message ( "DEBUG: <%s> passed first digit test" % cur_net_name[loc_parse_net_name:] , app_logfile ) loc_parse_net_name += 1 while ( ( loc_parse_net_name < len(cur_net_name) ) and ( cur_net_name[loc_parse_net_name].isdigit() ) ) : if debug_patterns : print_message ( "DEBUG: <%s> passed another digit test" % cur_net_name[loc_parse_net_name:] , app_logfile ) loc_parse_net_name += 1 elif ( net_name_pattern_field == chr_wildcard_diff ) : # require trailing _DIR or _CMP suffix_match = False for suffix_diff in lst_suffix_diff : # check if we match any suffix if ( cur_net_name[loc_parse_net_name:] == suffix_diff ) : # this also ensures it is the end suffix_match = True if debug_patterns : print_message ( "DEBUG: <%s> passed suffix test" % cur_net_name[loc_parse_net_name:] , app_logfile ) loc_parse_net_name += len (suffix_diff) # move parse location to the end of this pattern match if not suffix_match : # one match was required or we failed pattern_match_failed = True if debug_patterns : print_message ( "DEBUG: <%s> failed suffix test" % cur_net_name[loc_parse_net_name:] , app_logfile ) # ; sys.exit( 1 ) else : # we need to match this segment of the pattern loc_pattern = string.find ( cur_net_name[loc_parse_net_name:], net_name_pattern_field ) if pattern_match_strict : # we need to match this segment of the pattern at the current parsing location needs if ( loc_pattern == 0 ) : # move parse location to the end of this pattern match if debug_patterns : print_message ( "DEBUG: <%s> passed strict pattern test for <%s>" % ( cur_net_name[loc_parse_net_name:], net_name_pattern_field ) , app_logfile ) loc_parse_net_name += len (net_name_pattern_field) else : pattern_match_failed = True #if debug_patterns : print_message ( "DEBUG: <%s> failed strict pattern test for <%s>" % ( cur_net_name[loc_parse_net_name:], net_name_pattern_field ) , app_logfile ) else : # we need to match this segment anywhere if ( loc_pattern >= 0 ) : # move parse location to the end of this pattern match if debug_patterns : print_message ( "DEBUG: <%s> passed loose pattern test for <%s>" % ( cur_net_name[loc_parse_net_name:], net_name_pattern_field ) , app_logfile ) loc_parse_net_name = loc_pattern + len(net_name_pattern_field) else : pattern_match_failed = True #if debug_patterns : print_message ( "DEBUG: <%s> failed loose pattern test for <%s>" % ( cur_net_name[loc_parse_net_name:], net_name_pattern_field ) , app_logfile ) # no need to continue with next field if we already failed match if pattern_match_failed : break # we have gone through all the pattern fields, so we should be at end of the net name string to be strict if ( pattern_match_strict and not pattern_match_failed ) : if ( loc_parse_net_name != len (cur_net_name) ) : pattern_match_failed = True if debug_patterns : print_message ( "DEBUG: <%s> failed pattern strict length test at char %d" % ( cur_net_name , loc_parse_net_name ) , app_logfile ) # ; sys.exit( 1 ) else : if debug_patterns : print_message ( "DEBUG: <%s> passed pattern strict length test at char %d" % ( cur_net_name , loc_parse_net_name ) , app_logfile ) # As soon as we have matched one pattern we know we will record the net if not pattern_match_failed : record_this_net = True if debug_patterns : print_message ( "DEBUG: <%s> passed pattern test" % cur_net_name , app_logfile ) # no need to continue with next pattern if we already found a match break # After dealing with the net name, now deal with the wires and segments #---------------------------------------------------------------------- cur_net_wire_dict = {} # this will be the dictionary for sorting the wires for this net cur_wire_num = 0 # this will be incremented to become the index for each wire entry for this net cur_wire_and_segments = [] # this will be an array containing all the segments from a wire entry # read first line after the NET comment line_num += 1 input_trace_line = input_trace_file.readline () [:-1] # repeat for all statements in this net (i.e. until next comment) found_end_of_cur_net = False while ( ( not found_end_of_cur_net ) and ( not found_end_of_traces ) ) : # Add wire declaration or segment for current "wire" in this net cur_wire_and_segments.append ( input_trace_line ) #read next line line_num += 1 input_trace_line = input_trace_file.readline () [:-1] # look for the end of the file, # take note for now if ( ( input_trace_line[:len(str_last_line)] == str_last_line ) or ( input_trace_line == '' ) ) : found_end_of_traces = True # look for end of last wire for this net if ( input_trace_line[:1] == chr_comment_delim ) : found_end_of_cur_net = True # look for the end of the last segment e.g. beginning of a new wire segment or other reason if ( ( input_trace_line[:len_trcpath_keyword] in new_wire_keyword ) or ( found_end_of_cur_net ) or ( found_end_of_traces ) ) : cur_wire_num += 1 # record wire content collected earlier cur_net_wire_dict [ cur_wire_num ] = cur_wire_and_segments # start a new wire record cur_wire_and_segments = [] elif ( ( input_trace_line[:len_trcpath_keyword] != str_trcpath_seg ) and ( record_this_net ) ) : print_message ( " ERROR: found unexpected line type for NET <%s> at line #%d" % ( cur_net_name, line_num ) , app_logfile ) print_message ( " .....: %s " % input_trace_line , app_logfile ) print_message ( " Aborting" , app_logfile ) sys.exit( 1 ) # check... but there shouldn't have been a NET comment without wires anyway if ( len(cur_net_wire_dict) == 0 ) : print print_message ( " ERROR: Found a NET comment line without wire entries <%s>" % cur_net_comment , app_logfile ) # Here we must have a new line starting with a comment, so it'd better be a NET comment if ( ( input_trace_line[:len(str_net_line_start)] != str_net_line_start ) and ( not found_end_of_traces ) ) : print_message ( " ERROR: comment found but not for NET name at line #%d" % line_num , app_logfile ) print_message ( " .....: %s " % input_trace_line , app_logfile ) print_message ( " Aborting" , app_logfile ) sys.exit( 1 ) # Done with scanning the trace information for this net # record it if we had determined it matched one of the patterns #-------------------------------------------------------------- # sanity check: detect multiple NET entries for same net name if filtered_net_dict.has_key ( cur_net_name ) : print_message ( "ERROR: multiple NET entries for <%s>" % cur_net_name , app_logfile ) # If selected, add this net to the filtered dictionary if record_this_net : filtered_net_dict[ cur_net_name ]= [] for wire_num in range ( len(cur_net_wire_dict) ) : filtered_net_dict[ cur_net_name ].append( cur_net_wire_dict [ wire_num+1 ] ) # done scanning input_trace_file.close () print_message ( "Found %d matching NET entries\n" % len ( filtered_net_dict ) , app_logfile ) # obsolete debug case : no pattern means all nets and just spit back what was collected # that was an early debug test, and it was kept in case we need it again ##################### if ( len ( net_name_pattern_list ) == 0 ) : # special case : no pattern means copy all nets output_trace_file = open ( "trace_check.txt", "w" ) for net_name in sorted ( filtered_net_dict.keys() ) : output_trace_file.write ( "# NET '%s'\n" % net_name ) for wire in filtered_net_dict[net_name] : output_trace_file.write ( "%s\n" % wire[0] ) for segment_num in range ( 1, len(wire) ) : output_trace_file.write ( "%s\n" % wire[segment_num] ) output_trace_file.close () sys.exit(1) # Here comes the analysis of all the nets selected above ####################################################### else : print_message ( "Analyzing selected NET entries:" , app_logfile ) candidates_lst = [] # this will receive the list of all transition points (and extremities) # scan all nets #--------------- max_net_name_length = 0 # will come handy for formatting messages for net_name in sorted ( filtered_net_dict.keys() ) : print_message ( " Found Net Name <%s> " % net_name , app_logfile ) net_name_length = len ( net_name ) if ( net_name_length > max_net_name_length ) : max_net_name_length = net_name_length # scan all wires #--------------- wire_num = 0 # wire number within this net wire_sort_lst = [] # to record matching_wire_num, matching_code, start X, Y, end X, Y for wire in filtered_net_dict[net_name] : wire_num+=1 #print_message ( " <%s> " % wire[0] , app_logfile ) #print_message ( " made of %d segments " % len(wire) , app_logfile ) #for segment_num in range ( 1, len(wire) ) : #print_message ( " <%s> " % wire[segment_num] , app_logfile ) #The first elemetn is the WIR record, the rest are SEG wire_fields = string.split ( wire[0] ) # extract the relevant information wire_begX_tnano_str = wire_fields [ idx_wire_begX ] wire_begY_tnano_str = wire_fields [ idx_wire_begY ] wire_endX_tnano_str = wire_fields [ idx_wire_endX ] wire_endY_tnano_str = wire_fields [ idx_wire_endY ] wire_begX_mm_float = eval ( wire_begX_tnano_str ) / mentor_decanano_to_mm wire_begY_mm_float = eval ( wire_begY_tnano_str ) / mentor_decanano_to_mm wire_endX_mm_float = eval ( wire_endX_tnano_str ) / mentor_decanano_to_mm wire_endY_mm_float = eval ( wire_endY_tnano_str ) / mentor_decanano_to_mm wire_beg_layer_phys = wire[1].split()[idx_segm_layer] # read from the first segment wire_end_layer_phys = wire[len(wire)-1].split()[idx_segm_layer] # read from the last segment if ( wire_beg_layer_phys != wire_end_layer_phys ) : print_message ( "WARNING: Wire #%d Start and End are NOT on the same layer" % wire_num , app_logfile ) wire_end_layer_stack = drill_rules_dict [ wire_end_layer_phys ] [ idx_rules_cosm_name ] wire_beg_layer_stack = drill_rules_dict [ wire_beg_layer_phys ] [ idx_rules_cosm_name ] # determine if end points are in Named Regions #--------------------------------------------- message_beg_point = "" message_end_point = "" wire_beg_region_name = "" wire_end_region_name = "" for region_name in named_regions_dict.keys() : region_Xmin = named_regions_dict[ region_name ] [ idx_region_Xmin ] region_Xmax = named_regions_dict[ region_name ] [ idx_region_Xmax ] region_Ymin = named_regions_dict[ region_name ] [ idx_region_Ymin ] region_Ymax = named_regions_dict[ region_name ] [ idx_region_Ymax ] # see if Start point is in named region if ( ( wire_begX_mm_float >= region_Xmin ) and ( wire_begX_mm_float <= region_Xmax ) and ( wire_begY_mm_float >= region_Ymin ) and ( wire_begY_mm_float <= region_Ymax ) ) : if ( message_beg_point != "" ) : message_beg_point = "%s\n" % message_beg_point # add to any previous line (but there shouldn't be one) print_message ( "WARNING: Multiple Named Region match for Wire #%d Start point" % wire_num , app_logfile ) wire_beg_region_name = region_name message_beg_point = "%s Wire #%d Starts ( %6.2f, %6.2f ) within Region <%s> on layer %2s=<%s>" \ % ( message_beg_point, wire_num, wire_begX_mm_float, wire_begY_mm_float, wire_beg_region_name, wire_beg_layer_phys, wire_beg_layer_stack ) # see if End point is in named region if ( ( wire_endX_mm_float >= region_Xmin ) and ( wire_endX_mm_float <= region_Xmax ) and ( wire_endY_mm_float >= region_Ymin ) and ( wire_endY_mm_float <= region_Ymax ) ) : if ( message_end_point != "" ) : message_end_point = "%s\n" % message_end_point # add to any previous line (but there shouldn't be one) print_message ( "WARNING: Multiple Named Region match for Wire #%d End point" % wire_num , app_logfile ) wire_end_region_name = region_name message_end_point = "%s Wire #%d Ends ( %6.2f, %6.2f ) within Region <%s> on layer %2s=<%s>" \ % ( message_end_point, wire_num, wire_endX_mm_float, wire_endY_mm_float, wire_end_region_name, wire_end_layer_phys, wire_end_layer_stack ) # we print messages outside the region check loop to force message order, begin point before end # but we also flag point that were not within any named region if ( message_beg_point != "" ) : print_message ( message_beg_point, app_logfile ) else : print_message ( " WARNING: Wire #%d Start point ( %6.2f, %6.2f ) on layer %2s=<%s> is not within a Named Region" \ % ( wire_num, wire_endX_mm_float, wire_endY_mm_float, wire_end_layer_phys, wire_end_layer_stack) , app_logfile ) if ( message_end_point != "" ) : print_message ( message_end_point, app_logfile ) else : print_message ( " WARNING: Wire #%d End point ( %6.2f, %6.2f ) on layer %2s=<%s> is not within a Named Region" \ % ( wire_num, wire_endX_mm_float, wire_endY_mm_float, wire_end_layer_phys, wire_end_layer_stack) , app_logfile ) # # we looked at these nets and regions for some purpose so there should probably have been something to report # if ( ( message_beg_point == "" ) # and ( message_end_point == "" ) ) : # print_message ( "ERROR: Wire #%d neither Start nor End are within a Named Region" % wire_num , app_logfile ) # in any case, add all this wire information the the sort list wire_sort_lst.append ( [ [wire_begX_mm_float,wire_begY_mm_float], [wire_endX_mm_float,wire_endY_mm_float], wire_beg_region_name, wire_end_region_name, wire_beg_layer_phys, wire_end_layer_phys ] ) # (still on the same net) # now go through the wires to flag matching ends among wires and unmatched extremities #---------------------------------------------------------------------------- for cur_wire_num in range ( len(wire_sort_lst) ) : cur_wire_beg_match = [ sort_code_match_none, sort_code_match_none ] cur_wire_end_match = [ sort_code_match_none, sort_code_match_none ] for oth_wire_num in range ( len(wire_sort_lst) ) : if ( oth_wire_num != cur_wire_num ) : detected_error = False # does the other wire Start point match this wire Start point if ( wire_sort_lst[oth_wire_num][idx_sort_beg_coord] == wire_sort_lst[cur_wire_num][idx_sort_beg_coord] ) : if ( cur_wire_beg_match[idx_sort_match_code] == sort_code_match_none ) : cur_wire_beg_match[idx_sort_match_code] = sort_code_match_beg cur_wire_beg_match[idx_sort_wire_num] = oth_wire_num else : detected_error = True # does the other wire End point match this wire Start point if ( wire_sort_lst[oth_wire_num][idx_sort_end_coord] == wire_sort_lst[cur_wire_num][idx_sort_beg_coord] ) : if ( cur_wire_beg_match[idx_sort_match_code] == sort_code_match_none ) : cur_wire_beg_match[idx_sort_match_code] = sort_code_match_end cur_wire_beg_match[idx_sort_wire_num] = oth_wire_num else : detected_error = True if detected_error : print_message ( "ERROR: Wire #%d matches #%d but had also matched #%d" \ % ( cur_wire_num, oth_wire_num, cur_wire_beg_match[idx_sort_wire_num] ) , app_logfile ) detected_error = False # does the other wire Start point match this wire End point if ( wire_sort_lst[oth_wire_num][idx_sort_beg_coord] == wire_sort_lst[cur_wire_num][idx_sort_end_coord] ) : if ( cur_wire_end_match[idx_sort_match_code] == sort_code_match_none ) : cur_wire_end_match[idx_sort_match_code] = sort_code_match_beg cur_wire_end_match[idx_sort_wire_num] = oth_wire_num else : detected_error = True # does the other wire End point match this wire End point if ( wire_sort_lst[oth_wire_num][idx_sort_end_coord] == wire_sort_lst[cur_wire_num][idx_sort_end_coord] ) : if ( cur_wire_end_match[idx_sort_match_code] == sort_code_match_none ) : cur_wire_end_match[idx_sort_match_code] = sort_code_match_end cur_wire_end_match[idx_sort_wire_num] = oth_wire_num else : detected_error = True if detected_error : print_message ( "ERROR: Wire #%d matches #%d but had also matched #%d" \ % ( cur_wire_num, oth_wire_num, cur_wire_end_match[idx_sort_wire_num] ) , app_logfile ) wire_sort_lst[cur_wire_num].append ( cur_wire_beg_match ) wire_sort_lst[cur_wire_num].append ( cur_wire_end_match ) # (still on the same net) # Now we can follow the whole net trace path from beginning to end # and record all end points and transitions we walk through #--------------------------------------------------------------- # first go through the list again to find one extremity for this net net_start_wire_num = None for wire_num in range ( len(wire_sort_lst) ) : if ( wire_sort_lst[wire_num][idx_sort_beg_match][idx_sort_match_code] == sort_code_match_none ) : net_start_wire_num = wire_num net_start_coord = wire_sort_lst[net_start_wire_num][idx_sort_beg_coord] net_start_region = wire_sort_lst[net_start_wire_num][idx_sort_beg_region] net_start_layer = wire_sort_lst[net_start_wire_num][idx_sort_beg_layer] net_start_side = sort_code_match_beg break if ( wire_sort_lst[wire_num][idx_sort_end_match][idx_sort_match_code] == sort_code_match_none ) : net_start_wire_num = wire_num net_start_coord = wire_sort_lst[net_start_wire_num][idx_sort_end_coord] net_start_region = wire_sort_lst[net_start_wire_num][idx_sort_end_region] net_start_layer = wire_sort_lst[net_start_wire_num][idx_sort_end_layer] net_start_side = sort_code_match_end break # we should have found at least one unmatched end if ( net_start_wire_num == None ) : print_message ( "ERROR: Failed to find a starting point for this net, aborting" , app_logfile ) sys.exit (1) else : # find the rest of the path print_message ( " Path: at ( %9.5f, %9.5f ) Picked Net Start point from Wire #%d on layer %s " \ % ( net_start_coord[idx_coord_X], net_start_coord[idx_coord_Y], net_start_wire_num+1, net_start_layer ) , app_logfile ) # add net Start to transition candidate list #--------------------------------- if net_start_region : # Only points within named regions become candidates for further processing candidates_lst.append ( [ net_name, code_cand_extremity, net_start_coord, net_start_region, net_start_layer, net_start_wire_num ] ) # follow the path by hoping from wire to wire #--------------------------------- wire_switch_count = 0 cur_wire_num = net_start_wire_num cur_wire_side = net_start_side found_end = False while ( ( not found_end ) and ( wire_switch_count < len(wire_sort_lst) ) ) : if ( cur_wire_side == sort_code_match_beg ) : # we are at the start point of current wire, so the next wire is at the end point cur_wire_coord = wire_sort_lst[cur_wire_num][idx_sort_end_coord] cur_wire_region = wire_sort_lst[cur_wire_num][idx_sort_end_region] cur_wire_layer = wire_sort_lst[cur_wire_num][idx_sort_end_layer] nxt_wire_num = wire_sort_lst[cur_wire_num][idx_sort_end_match][idx_sort_wire_num] nxt_wire_side = wire_sort_lst[cur_wire_num][idx_sort_end_match][idx_sort_match_code] elif ( cur_wire_side == sort_code_match_end ) : # we are at the end point of current wire, so the next wire is at the start point cur_wire_coord = wire_sort_lst[cur_wire_num][idx_sort_beg_coord] cur_wire_region = wire_sort_lst[cur_wire_num][idx_sort_beg_region] cur_wire_layer = wire_sort_lst[cur_wire_num][idx_sort_beg_layer] nxt_wire_num = wire_sort_lst[cur_wire_num][idx_sort_beg_match][idx_sort_wire_num] nxt_wire_side = wire_sort_lst[cur_wire_num][idx_sort_beg_match][idx_sort_match_code] else : # can't happen, but... print_message ( "ERROR: Internal Error while unwinding through wires, aborting" , app_logfile ) sys.exit (1) #print wire_switch_count, cur_wire_num, cur_wire_side, cur_wire_layer, cur_wire_coord, nxt_wire_num, nxt_wire_side if ( nxt_wire_side == sort_code_match_beg ) : nxt_wire_layer = wire_sort_lst[nxt_wire_num][idx_sort_beg_layer] elif ( nxt_wire_side == sort_code_match_end ) : nxt_wire_layer = wire_sort_lst[nxt_wire_num][idx_sort_end_layer] else : found_end = True # show the switch from current to next wire if not found_end : print_message ( " Path: at ( %9.5f, %9.5f ) Switched from Wire #%d on layer %s to Wire #%d on layer %s" \ % ( cur_wire_coord[idx_coord_X], cur_wire_coord[idx_coord_Y], cur_wire_num+1, cur_wire_layer, nxt_wire_num+1, nxt_wire_layer ) , app_logfile ) # This is where we find and order the layer change if ( eval ( nxt_wire_layer ) > eval ( cur_wire_layer ) ) : cand_code = code_cand_change_layer cand_layer_smaller = cur_wire_layer cand_layer_bigger = nxt_wire_layer wire_num_smaller = cur_wire_num wire_num_bigger = nxt_wire_num elif ( nxt_wire_layer == cur_wire_layer ) : cand_code = code_cand_same_layer cand_layer_smaller = cur_wire_layer cand_layer_bigger = cand_layer_smaller wire_num_smaller = cur_wire_num wire_num_bigger = nxt_wire_num print_message ( "WARNING: Noticed change of wire without change of layer" , app_logfile ) else : # ( eval ( nxt_wire_layer ) < eval ( cur_wire_layer ) ) : cand_code = code_cand_change_layer cand_layer_smaller = nxt_wire_layer cand_layer_bigger = cur_wire_layer wire_num_smaller = nxt_wire_num wire_num_bigger = cur_wire_num # add this intermediate point to transition candidate list #--------------------------------------------- if cur_wire_region : # Only points within named regions become candidates for further processing candidates_lst.append ( [ net_name, cand_code, cur_wire_coord, cur_wire_region, cand_layer_smaller, cand_layer_bigger, wire_num_smaller, wire_num_bigger ] ) # get ready for next wire on this net cur_wire_num = nxt_wire_num cur_wire_side = nxt_wire_side wire_switch_count += 1 # we should have ended by finding the end after switching exactly one fewer as many times as there are wires if not found_end : print_message ( "ERROR: did not find an end to this net, aborting" , app_logfile ) sys.exit (1) if ( wire_switch_count != len(wire_sort_lst) - 1 ) : print_message ( "ERROR: did not follow all wires in this net, aborting" , app_logfile ) sys.exit (1) # Look at the side of the last current wire and the Net end will be at the other side net_end_wire_num = cur_wire_num if ( cur_wire_side == sort_code_match_beg ) : net_end_layer = wire_sort_lst[net_end_wire_num][idx_sort_end_layer] net_end_coord = wire_sort_lst[net_end_wire_num][idx_sort_end_coord] net_end_region = wire_sort_lst[net_end_wire_num][idx_sort_end_region] elif ( cur_wire_side == sort_code_match_end ) : net_end_layer = wire_sort_lst[net_end_wire_num][idx_sort_beg_layer] net_end_coord = wire_sort_lst[net_end_wire_num][idx_sort_beg_coord] net_end_region = wire_sort_lst[net_end_wire_num][idx_sort_beg_region] else : # can't happen, but... print_message ( "ERROR: Internal Error while unwinding net end, aborting" , app_logfile ) sys.exit (1) print_message ( " Path: at ( %9.5f, %9.5f ) found Net End point in Wire #%d on layer %s " \ % ( net_end_coord[idx_coord_X], net_end_coord[idx_coord_Y], net_end_wire_num+1, net_end_layer ) , app_logfile ) # add Net end to transition candidate list #------------------------------- if net_end_region : # Only points within named regions become candidates for further processing candidates_lst.append ( [ net_name, code_cand_extremity, net_end_coord, net_end_region, net_end_layer, net_end_wire_num ] ) # Open all the files we will be writing to ########################################## # and make a more convenient list of Mentor layers output_files = {} # the file handles output_layers = {} # the mentor layer names for circles drill_count = {} # the count of drill holes in each file for rule_cosm_name in drill_output_files_dict.keys() : drill_file_name = drill_output_files_dict[rule_cosm_name][idx_output_file_name] mentor_layer_name = drill_output_files_dict[rule_cosm_name][idx_output_mentor_layer] if ( drill_file_name != "" ) : output_files [ rule_cosm_name ] = open ( drill_file_name, "w" ) else : output_files [ rule_cosm_name ] = None # this makes it easy to check for non-existense # add header and tool name to any open file that is a Drill File if output_files[rule_cosm_name] : if ( rule_cosm_name not in ( key_drill_any, key_mentor_check ) ) : output_files[rule_cosm_name].write ( drill_file_header ) drill_count[rule_cosm_name] = 0 if ( mentor_layer_name != "" ) : output_layers [ rule_cosm_name ] = mentor_layer_name else : output_layers [ rule_cosm_name ] = None # make sure these were defined or add key with an empty value for easy check later if not drill_output_files_dict.has_key( key_mentor_check ) : output_files [ key_mentor_check ] = None if not drill_output_files_dict.has_key( key_drill_any ) : output_layers [ key_drill_any ] = None # go through list of recorded transition candidates # and find out what to do with each ######################################### print_message ( "\nScanning collected list of Net Extremities and Layer Transition candidates to determine Drill action:", app_logfile ) extr_count_dict = {} # will record a count of each type of extremity trans_count_dict = {} # will record a count of each type of layer transitions candidate_num = 1 for candidate in candidates_lst : candidate_name = candidate [ idx_cand_net_name ] candidate_name_padded = candidate_name + str_40_spaces[: max_net_name_length - len( candidate_name ) ] region_name = candidate [ idx_cand_region_name ] region_name_padded = region_name+ str_40_spaces[: max_region_name_length - len( region_name ) ] candidate_coord_X = candidate[idx_cand_coord][idx_coord_X] candidate_coord_Y = candidate[idx_cand_coord][idx_coord_Y] # Deal with extremities here #---------------------- if ( candidate[idx_cand_type] == code_cand_extremity ) : layer_phys_name = candidate [ idx_cand_layer ] layer_cosm_name = drill_rules_dict [ layer_phys_name ] [ idx_rules_cosm_name ] layer_cosm_name_padded = layer_cosm_name + str_40_spaces[: max_layer_name_length - len( layer_cosm_name ) ] print_message ( " Entry #%4d Net <%s> Type <%s> at ( %6.2f, %6.2f ) region <%s> Layer#%2s=<%s> Wire#%d" \ % ( candidate_num, candidate_name_padded, str_code_cand_type[ code_cand_extremity ], candidate_coord_X, candidate_coord_Y, region_name_padded, layer_phys_name, layer_cosm_name_padded, candidate[idx_cand_wire] ) , app_logfile ) # Add this candidate extremity to the summary count if not extr_count_dict.has_key(layer_phys_name) : extr_count_dict[layer_phys_name] = {} if not extr_count_dict[layer_phys_name].has_key(region_name) : extr_count_dict[layer_phys_name][region_name] = [ 0, "" ] extr_count_dict[layer_phys_name][region_name][idx_count_instance] += 1 # See if we have a rule for this case if ( drill_rules_dict.has_key (layer_phys_name ) and drill_rules_dict [layer_phys_name][idx_rules_drill_dict].has_key(key_net_extremity) ) : rule_cosm_name = drill_rules_dict [layer_phys_name][idx_rules_drill_dict][key_net_extremity] # See if the rule is an action (as opposed to ignoring it) if ( rule_cosm_name != Keyword_No_Drill ) : print_message ( " INFO: Applying Rule <%s>" % rule_cosm_name, app_logfile ) # Add to the appropriate drill file if output_files [ rule_cosm_name ] : drill_count[rule_cosm_name] += 1 # keep count output_files [ rule_cosm_name ].write ( drill_coord_line_format % ( candidate_coord_X * drill_mm_to_XYcoord_scale , candidate_coord_Y * drill_mm_to_XYcoord_scale ) ) # Add to the Mentor Check file if output_files [ key_mentor_check ] : if output_layers [ rule_cosm_name ] : mentor_layer_name = output_layers [ rule_cosm_name ] mentor_layer_name_padding = str_40_spaces[: max_mentor_layer_name_length - len ( mentor_layer_name ) ] output_files [ key_mentor_check ].write ( '$$circle( "%s"%s, %6.2f, %6.2f, 0.4, 0 );\n' \ % ( mentor_layer_name, mentor_layer_name_padding, candidate_coord_X, candidate_coord_Y ) ) if output_layers [ key_drill_any ] : mentor_layer_any = output_layers [ key_drill_any ] mentor_layer_any_padding = str_40_spaces[: max_mentor_layer_name_length - len ( mentor_layer_any ) ] output_files [ key_mentor_check ].write ( '$$circle( "%s"%s, %6.2f, %6.2f, 0.4, 0 );\n' \ % ( mentor_layer_any, mentor_layer_any_padding, candidate_coord_X, candidate_coord_Y ) ) else : print_message ( " INFO: No Action taken" , app_logfile ) else : print_message ( " ERROR: No Rule given for Net Extremity on this layer" , app_logfile ) extr_count_dict[layer_phys_name][region_name][idx_missing_rule_flag] = str_rule_missing # Deal with Wire/Layer transition candidates here #--------------------------------- else : layer_smaller_phys_name = candidate[idx_cand_layer_smaller] layer_smaller_cosm_name = drill_rules_dict [ layer_smaller_phys_name ] [ idx_rules_cosm_name ] layer_smaller_cosm_name_padded = layer_smaller_cosm_name + str_40_spaces[: max_layer_name_length - len( layer_smaller_cosm_name ) ] layer_bigger_phys_name = candidate[idx_cand_layer_bigger] layer_bigger_cosm_name = drill_rules_dict [ layer_bigger_phys_name ] [ idx_rules_cosm_name ] layer_bigger_cosm_name_padded = layer_bigger_cosm_name + str_40_spaces[: max_layer_name_length - len( layer_bigger_cosm_name ) ] print_message ( " Entry #%4d Net <%s> Type <%s> at ( %6.2f, %6.2f ) region <%s> from Layer#%2s=<%s> to #%2s=<%s> Wire#%d to #%d" \ % ( candidate_num, candidate_name_padded, str_code_cand_type[ candidate[idx_cand_type] ], candidate_coord_X, candidate_coord_Y, region_name_padded, layer_smaller_phys_name, layer_smaller_cosm_name_padded, layer_bigger_phys_name, layer_bigger_cosm_name_padded, candidate[idx_cand_wire_from], candidate[idx_cand_wire_to] ) , app_logfile ) # Add this candidate transtion to the summary count if not trans_count_dict.has_key(layer_smaller_phys_name) : trans_count_dict[layer_smaller_phys_name] = {} if not trans_count_dict[layer_smaller_phys_name].has_key(layer_bigger_phys_name) : trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name] = {} if not trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name].has_key(region_name) : trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name][region_name] = [ 0, "" ] trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name][region_name][idx_count_instance] += 1 # See if we have a rule for this case if ( drill_rules_dict.has_key (layer_smaller_phys_name ) and drill_rules_dict [layer_smaller_phys_name][idx_rules_drill_dict].has_key(layer_bigger_phys_name) ) : rule_cosm_name = drill_rules_dict [layer_smaller_phys_name][idx_rules_drill_dict][layer_bigger_phys_name] # See if the rule is an action (as opposed to ignoring it) if ( rule_cosm_name != Keyword_No_Drill ) : print_message ( " INFO: Applying Rule <%s>" % rule_cosm_name, app_logfile ) # Add to the appropriate drill file if output_files [ rule_cosm_name ] : drill_count[rule_cosm_name] += 1 # keep count output_files [ rule_cosm_name ].write ( drill_coord_line_format % ( candidate_coord_X * drill_mm_to_XYcoord_scale , candidate_coord_Y * drill_mm_to_XYcoord_scale ) ) # Add to the Mentor Check file if output_files [ key_mentor_check ] : if output_layers [ rule_cosm_name ] : mentor_layer_name = output_layers [ rule_cosm_name ] mentor_layer_name_padding = str_40_spaces[: max_mentor_layer_name_length - len ( mentor_layer_name ) ] output_files [ key_mentor_check ].write ( '$$circle( "%s"%s, %6.2f, %6.2f, 0.4, 0 );\n' \ % ( mentor_layer_name, mentor_layer_name_padding, candidate_coord_X, candidate_coord_Y ) ) if output_layers [ key_drill_any ] : mentor_layer_any = output_layers [ key_drill_any ] mentor_layer_any_padding = str_40_spaces[: max_mentor_layer_name_length - len ( mentor_layer_any ) ] output_files [ key_mentor_check ].write ( '$$circle( "%s"%s, %6.2f, %6.2f, 0.4, 0 );\n' \ % ( mentor_layer_any, mentor_layer_any_padding, candidate_coord_X, candidate_coord_Y ) ) else : print_message ( " INFO: No Action taken" , app_logfile ) else : print_message ( " ERROR: No Rule given for Transition between these two layers" , app_logfile ) trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name][region_name][idx_missing_rule_flag] = str_rule_missing # go to next candidate candidate_num += 1 # report summary ################# print_message ( "\nSummary of Candidate points found" , app_logfile ) region_unused_dashed = str_40_dashes[: max_region_name_length ] layer_unused_dashed = str_40_dashes[: max_layer_name_length ] table_horizontal_border = " |------------+---------------+-%s-+------%s-+------%s-|" \ % ( region_unused_dashed, layer_unused_dashed, layer_unused_dashed ) print_message ( table_horizontal_border, app_logfile ) # list all extremities for layer in sorted ( extr_count_dict.iteritems(), key=lambda x:"%2s" % x[0] ) : layer_phys_name = layer[0] for region in sorted ( extr_count_dict[layer_phys_name].iteritems(), key= lambda x: x[0] ) : region_name = region[0] region_name_padded = region_name + str_40_spaces[: max_region_name_length - len( region_name ) ] layer_cosm_name = drill_rules_dict [ layer_phys_name ] [ idx_rules_cosm_name ] layer_cosm_name_padded = layer_cosm_name + str_40_spaces[: max_layer_name_length - len( layer_cosm_name ) ] layer_unused_padded = str_40_spaces[: max_layer_name_length ] print_message ( " | Extremity | %3d instances | %s | %2s = %s | %s | %s" \ % ( extr_count_dict[layer_phys_name][region_name][idx_count_instance], region_name_padded, layer_phys_name, layer_cosm_name_padded, layer_unused_padded, extr_count_dict[layer_phys_name][region_name][idx_missing_rule_flag] ) , app_logfile ) print_message ( table_horizontal_border, app_logfile ) # list all transitions for layer_smaller in sorted ( trans_count_dict.iteritems(), key=lambda x:"%2s" % x[0] ) : layer_smaller_phys_name = layer_smaller[0] for layer_bigger in sorted ( trans_count_dict[layer_smaller_phys_name].iteritems(), key= lambda x:"%2s" % x[0] ) : layer_bigger_phys_name = layer_bigger[0] for region in sorted ( trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name].iteritems(), key= lambda x: x[0] ) : region_name = region[0] region_name_padded = region_name + str_40_spaces[: max_region_name_length - len( region_name ) ] layer_smaller_cosm_name = drill_rules_dict [ layer_smaller_phys_name ] [ idx_rules_cosm_name ] layer_smaller_cosm_name_padded = layer_smaller_cosm_name + str_40_spaces[: max_layer_name_length - len( layer_smaller_cosm_name ) ] layer_bigger_cosm_name = drill_rules_dict [ layer_bigger_phys_name ] [ idx_rules_cosm_name ] layer_bigger_cosm_name_padded = layer_bigger_cosm_name + str_40_spaces[: max_layer_name_length - len( layer_bigger_cosm_name ) ] print_message ( " | Transition | %3d instances | %s | %2s = %s | %2s = %s | %s" \ % ( trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name][region_name][idx_count_instance], region_name_padded, layer_smaller_phys_name, layer_smaller_cosm_name_padded, layer_bigger_phys_name, layer_bigger_cosm_name_padded, trans_count_dict[layer_smaller_phys_name][layer_bigger_phys_name][region_name][idx_missing_rule_flag] ) , app_logfile ) print_message ( table_horizontal_border, app_logfile ) # close all files ################# print_message ( "\nClosing Drill Files" , app_logfile ) for rule_cosm_name in sorted ( drill_output_files_dict.keys() ) : if output_files[rule_cosm_name] : # add trailer to any open file that is a Drill File if ( rule_cosm_name not in ( key_drill_any, key_mentor_check ) ) : output_files[rule_cosm_name].write ( drill_file_trailer ) drill_file_name = drill_output_files_dict[rule_cosm_name][idx_output_file_name] drill_file_name_padding = str_40_spaces[: max_drill_file_name_length - len( drill_file_name ) ] print_message ( " <%s>%s contains %3d drill locations" % ( drill_file_name, drill_file_name_padding, drill_count[rule_cosm_name] ), app_logfile ) output_files[rule_cosm_name].close () # Done ##################################### print_message ( "\nDone\n" , app_logfile ) sys.exit( 1 )