diff options
Diffstat (limited to 'src/readlef.c')
-rw-r--r-- | src/readlef.c | 3436 |
1 files changed, 3436 insertions, 0 deletions
diff --git a/src/readlef.c b/src/readlef.c new file mode 100644 index 0000000..21a8b1d --- /dev/null +++ b/src/readlef.c @@ -0,0 +1,3436 @@ +/* + * lef.c -- + * + * This module incorporates the LEF/DEF format for standard-cell routing + * route. + * + * Version 0.1 (September 26, 2003): LEF input handling. Includes creation + * of cells from macro statements, handling of pins, ports, obstructions, and + * associated geometry. + * + * Written by Tim Edwards, Open Circuit Design + * Modified June 2011 for use with qrouter. + * Modified November 2018 for use with qflow. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <sys/time.h> +#include <math.h> + +#include "hash.h" +#include "readlef.h" + +/*----------------------------------------------------------------------*/ +/* Global variables */ +/*----------------------------------------------------------------------*/ + +GATE GateInfo = NULL; // standard cell macro information +u_char Verbose = 0; +char delimiter; // opening bus delimiter; + +struct hashtable MacroTable; + +/*----------------------------------------------------------------------*/ + +/* Current line number for reading */ +int lefCurrentLine = 0; + +/* Information about routing layers */ +LefList LefInfo = NULL; + +/* Information about what vias to use */ +LinkedStringPtr AllowedVias = NULL; + +/* Gate information is in the linked list GateInfo, imported */ + +/* + *--------------------------------------------------------- + * Initialize hash table of cells + *--------------------------------------------------------- + */ + +static void +LefHashInit(void) +{ + /* Initialize the macro hash table */ + InitializeHashTable(&MacroTable, SMALLHASHSIZE); +} + +/* + *--------------------------------------------------------- + * Add macro to Lef cell macro hash table + *--------------------------------------------------------- + */ + +static void +LefHashMacro(GATE gateginfo) +{ + HashPtrInstall(gateginfo->gatename, gateginfo, &MacroTable); +} + +/*--------------------------------------------------------- + * Lookup -- + * Searches a table of strings to find one that matches a given + * string. It's useful mostly for command lookup. + * + * Only the portion of a string in the table up to the first + * blank character is considered significant for matching. + * + * Results: + * If str is the same as + * or an unambiguous abbreviation for one of the entries + * in table, then the index of the matching entry is returned. + * If str is not the same as any entry in the table, but + * an abbreviation for more than one entry, + * then -1 is returned. If str doesn't match any entry, then + * -2 is returned. Case differences are ignored. + * + * NOTE: + * Table entries need no longer be in alphabetical order + * and they need not be lower case. The irouter command parsing + * depends on these features. + * + * Side Effects: + * None. + *--------------------------------------------------------- + */ + +int +Lookup(str, table) + char *str; /* Pointer to a string to be looked up */ + char *(table[]); /* Pointer to an array of string pointers + * which are the valid commands. + * The end of + * the table is indicated by a NULL string. + */ +{ + int match = -2; /* result, initialized to -2 = no match */ + int pos; + int ststart = 0; + + /* search for match */ + for (pos=0; table[pos] != NULL; pos++) + { + char *tabc = table[pos]; + char *strc = &(str[ststart]); + while (*strc!='\0' && *tabc!=' ' && + ((*tabc==*strc) || + (isupper(*tabc) && islower(*strc) && (tolower(*tabc)== *strc))|| + (islower(*tabc) && isupper(*strc) && (toupper(*tabc)== *strc)) )) + { + strc++; + tabc++; + } + + + if (*strc=='\0') + { + /* entry matches */ + if(*tabc==' ' || *tabc=='\0') + { + /* exact match - record it and terminate search */ + match = pos; + break; + } + else if (match == -2) + { + /* inexact match and no previous match - record this one + * and continue search */ + match = pos; + } + else + { + /* previous match, so string is ambiguous unless exact + * match exists. Mark ambiguous for now, and continue + * search. + */ + match = -1; + } + } + } + return(match); +} + +/* + * ---------------------------------------------------------------------------- + * LookupFull -- + * + * Look up a string in a table of pointers to strings. The last + * entry in the string table must be a NULL pointer. + * This is much simpler than Lookup() in that it does not + * allow abbreviations. It does, however, ignore case. + * + * Results: + * Index of the name supplied in the table, or -1 if the name + * is not found. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------------- + */ + +int +LookupFull(name, table) + char *name; + char **table; +{ + char **tp; + + for (tp = table; *tp; tp++) + { + if (strcmp(name, *tp) == 0) + return (tp - table); + else + { + char *sptr, *tptr; + for (sptr = name, tptr = *tp; ((*sptr != '\0') && (*tptr != '\0')); + sptr++, tptr++) + if (toupper(*sptr) != toupper(*tptr)) + break; + if ((*sptr == '\0') && (*tptr == '\0')) + return (tp - table); + } + } + + return (-1); +} + + +/* + *------------------------------------------------------------ + * + * LefNextToken -- + * + * Move to the next token in the stream input. + * If "ignore_eol" is FALSE, then the end-of-line character + * "\n" will be returned as a token when encountered. + * Otherwise, end-of-line will be ignored. + * + * Results: + * Pointer to next token to parse + * + * Side Effects: + * May read a new line from the specified file. + * + * Warnings: + * The return result of LefNextToken will be overwritten by + * subsequent calls to LefNextToken if more than one line of + * input is parsed. + * + *------------------------------------------------------------ + */ + +char * +LefNextToken(FILE *f, u_char ignore_eol) +{ + static char line[LEF_LINE_MAX + 2]; /* input buffer */ + static char *nexttoken = NULL; /* pointer to next token */ + static char *curtoken; /* pointer to current token */ + static char eol_token='\n'; + + /* Read a new line if necessary */ + + if (nexttoken == NULL) + { + for(;;) + { + if (fgets(line, LEF_LINE_MAX + 1, f) == NULL) return NULL; + lefCurrentLine++; + curtoken = line; + while (isspace(*curtoken) && (*curtoken != '\n') && (*curtoken != '\0')) + curtoken++; /* skip leading whitespace */ + + if ((*curtoken != '#') && (*curtoken != '\n') && (*curtoken != '\0')) + { + nexttoken = curtoken; + break; + } + } + if (!ignore_eol) + return &eol_token; + } + else + curtoken = nexttoken; + + /* Find the next token; set to NULL if none (end-of-line). */ + /* Treat quoted material as a single token */ + + if (*nexttoken == '\"') { + nexttoken++; + while (((*nexttoken != '\"') || (*(nexttoken - 1) == '\\')) && + (*nexttoken != '\0')) { + if (*nexttoken == '\n') { + if (fgets(nexttoken + 1, LEF_LINE_MAX - + (size_t)(nexttoken - line), f) == NULL) + return NULL; + } + nexttoken++; /* skip all in quotes (move past current token) */ + } + if (*nexttoken == '\"') + nexttoken++; + } + else { + while (!isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n')) + nexttoken++; /* skip non-whitespace (move past current token) */ + } + + /* Terminate the current token */ + if (*nexttoken != '\0') *nexttoken++ = '\0'; + + while (isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n')) + nexttoken++; /* skip any whitespace */ + + if ((*nexttoken == '#') || (*nexttoken == '\n') || (*nexttoken == '\0')) + nexttoken = NULL; + + return curtoken; +} + +/* + *------------------------------------------------------------ + * + * LefError -- + * + * Print an error message (via fprintf) giving the line + * number of the input file on which the error occurred. + * + * "type" should be either LEF_ERROR or LEF_WARNING (or DEF_*). + * + * Results: + * None. + * + * Side Effects: + * Prints to the output (stderr). + * + *------------------------------------------------------------ + */ + +void +LefError(int type, char *fmt, ...) +{ + static int fatal = 0; + static int nonfatal = 0; + char lefordef = 'L'; + int errors; + va_list args; + + if (Verbose == 0) return; + + if ((type == DEF_WARNING) || (type == DEF_ERROR)) lefordef = 'D'; + + errors = fatal + nonfatal; + if (fmt == NULL) /* Special case: report any errors and reset */ + { + if (errors > 0) + { + fprintf(stdout, "%cEF Read: encountered %d error%s and %d warning%s total.\n", + lefordef, + fatal, (fatal == 1) ? "" : "s", + nonfatal, (nonfatal == 1) ? "" : "s"); + fatal = 0; + nonfatal = 0; + } + return; + } + + if (errors < LEF_MAX_ERRORS) + { + fprintf(stderr, "%cEF Read, Line %d: ", lefordef, lefCurrentLine); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fflush(stderr); + } + else if (errors == LEF_MAX_ERRORS) + fprintf(stderr, "%cEF Read: Further errors/warnings will not be reported.\n", + lefordef); + + if ((type == LEF_ERROR) || (type == DEF_ERROR)) + fatal++; + else if ((type == LEF_WARNING) || (type == DEF_WARNING)) + nonfatal++; +} + +/* + *------------------------------------------------------------ + * + * LefParseEndStatement -- + * + * Check if the string passed in "lineptr" contains the + * appropriate matching keyword. Sections in LEF files + * should end with "END (keyword)" or "END". To check + * against the latter case, "match" should be NULL. + * + * Results: + * TRUE if the line matches the expected end statement, + * FALSE if not. + * + * Side effects: + * None. + * + *------------------------------------------------------------ + */ + +u_char +LefParseEndStatement(FILE *f, char *match) +{ + char *token; + int keyword; + char *match_name[2]; + + match_name[0] = match; + match_name[1] = NULL; + + token = LefNextToken(f, (match == NULL) ? FALSE : TRUE); + if (token == NULL) + { + LefError(LEF_ERROR, "Bad file read while looking for END statement\n"); + return FALSE; + } + + /* END or ENDEXT */ + if ((*token == '\n') && (match == NULL)) return TRUE; + + /* END <section_name> */ + else { + keyword = LookupFull(token, match_name); + if (keyword == 0) + return TRUE; + else + return FALSE; + } +} + +/* + *------------------------------------------------------------ + * + * LefSkipSection -- + * + * Skip to the "END" record of a LEF input section + * String "section" must follow the "END" statement in + * the file to be considered a match; however, if + * section = NULL, then "END" must have no strings + * following. + * + * Results: + * None. + * + * Side Effects: + * Reads input from the specified file. Prints an + * error message if the expected END record cannot + * be found. + * + *------------------------------------------------------------ + */ + +void +LefSkipSection(FILE *f, char *section) +{ + char *token; + int keyword; + static char *end_section[] = { + "END", + "ENDEXT", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + if ((keyword = Lookup(token, end_section)) == 0) + { + if (LefParseEndStatement(f, section)) + return; + } + else if (keyword == 1) + { + if (!strcmp(section, "BEGINEXT")) + return; + } + } + + LefError(LEF_ERROR, "Section %s has no END record!\n", section); + return; +} + +/* + *------------------------------------------------------------ + * + * lefFindCell -- + * + * "name" is the name of the cell to search for. + * Returns the GATE entry for the cell from the GateInfo + * hash table. + * + *------------------------------------------------------------ + */ + +GATE +lefFindCell(char *name) +{ + GATE gateginfo; + + gateginfo = (GATE)HashLookup(name, &MacroTable); + return gateginfo; +} + +/* + *------------------------------------------------------------ + * + * LefLower -- + * + * Convert a token in a LEF or DEF file to all-lowercase. + * + *------------------------------------------------------------ + */ + +char * +LefLower(char *token) +{ + char *tptr; + + for (tptr = token; *tptr != '\0'; tptr++) + *tptr = tolower(*tptr); + + return token; +} + +LefList +LefRedefined(LefList lefl, char *redefname) +{ + LefList slef, newlefl; + char *altName; + int records; + DSEG drect; + + /* check if more than one entry points to the same */ + /* lefLayer record. If so, we will also record the */ + /* name of the first type that is not the same as */ + /* "redefname". */ + + records = 0; + altName = NULL; + + for (slef = LefInfo; slef; slef = slef->next) { + if (slef == lefl) + records++; + if (altName == NULL) + if (strcmp(slef->lefName, redefname)) + altName = (char *)slef->lefName; + } + if (records == 1) + { + /* Only one name associated with the record, so */ + /* just clear all the allocated information. */ + + while (lefl->info.via.lr) { + drect = lefl->info.via.lr->next; + free(lefl->info.via.lr); + lefl->info.via.lr = drect; + } + newlefl = lefl; + } + else + { + slef = LefFindLayer(redefname); + + newlefl = (LefList)malloc(sizeof(lefLayer)); + newlefl->lefName = strdup(newlefl->lefName); + + newlefl->next = LefInfo; + LefInfo = newlefl; + + /* If the canonical name of the original entry */ + /* is "redefname", then change it. */ + + if (!strcmp(slef->lefName, redefname)) + if (altName != NULL) + slef->lefName = altName; + } + newlefl->type = -1; + newlefl->obsType = -1; + newlefl->info.via.area.x1 = 0.0; + newlefl->info.via.area.x2 = 0.0; + newlefl->info.via.area.y1 = 0.0; + newlefl->info.via.area.y2 = 0.0; + newlefl->info.via.area.layer = -1; + newlefl->info.via.cell = (GATE)NULL; + newlefl->info.via.lr = (DSEG)NULL; + + return newlefl; +} + +/* + *------------------------------------------------------------ + * Find a layer record in the list of layers + *------------------------------------------------------------ + */ + +LefList +LefFindLayer(char *token) +{ + LefList lefl, rlefl; + + if (token == NULL) return NULL; + rlefl = (LefList)NULL; + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (!strcmp(lefl->lefName, token)) { + rlefl = lefl; + break; + } + } + return rlefl; +} + +/* + *------------------------------------------------------------ + * Find a layer record in the list of layers, by layer number + *------------------------------------------------------------ + */ + +LefList +LefFindLayerByNum(int layer) +{ + LefList lefl, rlefl; + + rlefl = (LefList)NULL; + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->type == layer) { + rlefl = lefl; + break; + } + } + return rlefl; +} + +/* + *------------------------------------------------------------ + * Find a layer record in the list of layers, and return the + * layer number. + *------------------------------------------------------------ + */ + +int +LefFindLayerNum(char *token) +{ + LefList lefl; + + lefl = LefFindLayer(token); + if (lefl) + return lefl->type; + else + return -1; +} + +/* + *--------------------------------------------------------------- + * Find the maximum layer number defined by the LEF file + * This includes layer numbers assigned to both routes and + * via cut layers. + *--------------------------------------------------------------- + */ + +int +LefGetMaxLayer(void) +{ + int maxlayer = -1; + LefList lefl; + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->type > maxlayer) + maxlayer = lefl->type; + } + return (maxlayer + 1); +} + +/* + *--------------------------------------------------------------- + * Find the maximum routing layer number defined by the LEF file + * Note that as defined, this returns value (layer_index + 1). + *--------------------------------------------------------------- + */ + +int +LefGetMaxRouteLayer(void) +{ + int maxlayer = -1; + LefList lefl; + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass != CLASS_ROUTE) continue; + if (lefl->type > maxlayer) + maxlayer = lefl->type; + } + return (maxlayer + 1); +} + +/* + *------------------------------------------------------------ + * Return the route keepout area, defined as the route space + * plus 1/2 the route width. This is the distance outward + * from an obstruction edge within which one cannot place a + * route. + * + * If no route layer is defined, then we pick up the value + * from information in the route.cfg file (if any). Here + * we define it as the route pitch less 1/2 the route width, + * which is the same as above if the route pitch has been + * chosen for minimum spacing. + * + * If all else fails, return zero. + *------------------------------------------------------------ + */ + +double +LefGetRouteKeepout(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.width / 2.0 + + lefl->info.route.spacing->spacing; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Similar routine to the above. Return the route width for + * a route layer. Return value in microns. If there is no + * LEF file information about the route width, then return + * half of the minimum route pitch. + *------------------------------------------------------------ + */ + +double +LefGetRouteWidth(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.width; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Similar to the above, return the width of a via. Arguments + * are the via record, the layer to check the width of, and + * direction "dir" = 1 for height and 0 for width. + *------------------------------------------------------------ + */ + +double +LefGetViaWidth(LefList lefl, int layer, int dir) +{ + double width, maxwidth; + DSEG lrect; + + maxwidth = 0.0; + + if (lefl->lefClass == CLASS_VIA) { + if (lefl->info.via.area.layer == layer) { + if (dir) + width = lefl->info.via.area.y2 - lefl->info.via.area.y1; + else + width = lefl->info.via.area.x2 - lefl->info.via.area.x1; + if (width > maxwidth) maxwidth = width; + } + for (lrect = lefl->info.via.lr; lrect; lrect = lrect->next) { + if (lrect->layer == layer) { + if (dir) + width = lrect->y2 - lrect->y1; + else + width = lrect->x2 - lrect->x1; + if (width > maxwidth) maxwidth = width; + } + } + } + return maxwidth / 2; +} + +/* + *------------------------------------------------------------ + * Similar routine to the above. Return the route offset for + * a route layer. Return value in microns. If there is no + * LEF file information about the route offset, then return + * half of the minimum route pitch. + *------------------------------------------------------------ + */ + +double +LefGetRouteOffset(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + o = lefl->info.route.hdirection; + if (o == TRUE) + return lefl->info.route.offsety; + else + return lefl->info.route.offsetx; + } + } + return 0.0; +} + +double +LefGetRouteOffsetX(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.offsetx; + } + } + return 0.0; +} + +double +LefGetRouteOffsetY(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.offsety; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Find and return the minimum metal area requirement for a + * route layer. + *------------------------------------------------------------ + */ + +double +LefGetRouteMinArea(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.minarea; + } + } + return 0.0; /* Assume no minimum area requirement */ +} + +/* + *------------------------------------------------------------ + * Get route spacing rule (minimum width) + *------------------------------------------------------------ + */ + +double +LefGetRouteSpacing(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + if (lefl->info.route.spacing) + return lefl->info.route.spacing->spacing; + else + return 0.0; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Find route spacing to a metal layer of specific width + *------------------------------------------------------------ + */ + +double +LefGetRouteWideSpacing(int layer, double width) +{ + LefList lefl; + lefSpacingRule *srule; + double spacing; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + // Prepare a default in case of bad values + spacing = lefl->info.route.spacing->spacing; + for (srule = lefl->info.route.spacing; srule; srule = srule->next) { + if (srule->width > width) break; + spacing = srule->spacing; + } + return spacing; + } + } + return 0.0; +} + +/* + *----------------------------------------------------------------- + * Get the route pitch in the preferred direction for a given layer + *----------------------------------------------------------------- + */ + +double +LefGetRoutePitch(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + o = lefl->info.route.hdirection; + if (o == TRUE) + return lefl->info.route.pitchy; + else + return lefl->info.route.pitchx; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Get the route pitch in X for a given layer + *------------------------------------------------------------ + */ + +double +LefGetRoutePitchX(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.pitchx; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Get the route pitch in Y for a given layer + *------------------------------------------------------------ + */ + +double +LefGetRoutePitchY(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.pitchy; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Set the route pitch in X for a given layer + *------------------------------------------------------------ + */ + +void +LefSetRoutePitchX(int layer, double value) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + lefl->info.route.pitchx = value; + } + } +} + +/* + *------------------------------------------------------------ + * Set the route pitch in Y for a given layer + *------------------------------------------------------------ + */ + +void +LefSetRoutePitchY(int layer, double value) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + lefl->info.route.pitchy = value; + } + } +} + +/* + *------------------------------------------------------------ + * Get the route name for a given layer + *------------------------------------------------------------ + */ + +char * +LefGetRouteName(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->lefName; + } + } + return NULL; +} + +/* + *------------------------------------------------------------ + * Get the route orientation for the given layer, + * where the result is 1 for horizontal, 0 for vertical, and + * -1 if the layer is not found. + *------------------------------------------------------------ + */ + +int +LefGetRouteOrientation(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return (int)lefl->info.route.hdirection; + } + } + return -1; +} + +/* + *------------------------------------------------------------ + * Get the route resistance and capacitance information. + * Fill in the pointer values with the relevant information. + * Return 0 on success, -1 if the layer is not found. + *------------------------------------------------------------ + */ + +int +LefGetRouteRCvalues(int layer, double *areacap, double *edgecap, + double *respersq) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + *areacap = (double)lefl->info.route.areacap; + *edgecap = (double)lefl->info.route.edgecap; + *respersq = (double)lefl->info.route.respersq; + return 0; + } + } + return -1; +} + +/* + *------------------------------------------------------------ + * Get the antenna violation area ratio for the given layer. + *------------------------------------------------------------ + */ + +double +LefGetRouteAreaRatio(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.antenna; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Get the antenna violation area calculation method for the + * given layer. + *------------------------------------------------------------ + */ + +u_char +LefGetRouteAntennaMethod(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.method; + } + } + return CALC_NONE; +} + +/* + *------------------------------------------------------------ + * Get the route metal layer thickness (if any is defined) + *------------------------------------------------------------ + */ + +double +LefGetRouteThickness(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.thick; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * LefReadLayers -- + * + * Read a LEF "LAYER" record from the file. + * If "obstruct" is TRUE, returns the layer mapping + * for obstruction geometry as defined in the + * technology file (if it exists), and up to two + * types are returned (the second in the 3rd argument + * pointer). + * + * Results: + * Returns layer number or -1 if no matching type is found. + * + * Side Effects: + * Reads input from file f; + * + *------------------------------------------------------------ + */ + +int +LefReadLayers(f, obstruct, lreturn) + FILE *f; + u_char obstruct; + int *lreturn; +{ + char *token; + int curlayer = -1; + LefList lefl = NULL; + + token = LefNextToken(f, TRUE); + if (*token == ';') + { + LefError(LEF_ERROR, "Bad Layer statement\n"); + return -1; + } + else + { + lefl = LefFindLayer(token); + if (lefl) + { + if (obstruct) + { + /* Use the obstruction type, if it is defined */ + curlayer = lefl->obsType; + if ((curlayer < 0) && (lefl->lefClass != CLASS_IGNORE)) + curlayer = lefl->type; + else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT) + if (lreturn) *lreturn = lefl->info.via.obsType; + } + else + { + if (lefl->lefClass != CLASS_IGNORE) + curlayer = lefl->type; + } + } + if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE))) + { + /* CLASS_VIA in lefl record is a cut, and the layer */ + /* geometry is ignored for the purpose of routing. */ + + if (lefl && (lefl->lefClass == CLASS_CUT)) { + int cuttype; + + /* By the time a cut layer is being requested, */ + /* presumably from a VIA definition, the route */ + /* layers should all be defined, so start */ + /* assigning layers to cuts. */ + + /* If a cut layer is found with an unassigned number, */ + /* then assign it here. */ + cuttype = LefGetMaxLayer(); + if (cuttype < MAX_TYPES) { + lefl->type = cuttype; + curlayer = cuttype; + } + else + LefError(LEF_WARNING, "Too many cut types; type \"%s\" ignored.\n", + token); + } + else if ((!lefl) || (lefl->lefClass != CLASS_VIA)) + LefError(LEF_ERROR, "Don't know how to parse layer \"%s\"\n", token); + } + } + return curlayer; +} + +/* + *------------------------------------------------------------ + * LefReadLayer -- + * + * Read a LEF "LAYER" record from the file. + * If "obstruct" is TRUE, returns the layer mapping + * for obstruction geometry as defined in the + * technology file (if it exists). + * + * Results: + * Returns a layer number or -1 if no match is found. + * + * Side Effects: + * Reads input from file f; + * + *------------------------------------------------------------ + */ + +int +LefReadLayer(FILE *f, u_char obstruct) +{ + return LefReadLayers(f, obstruct, (int *)NULL); +} + +/* + *------------------------------------------------------------ + * LefReadRect -- + * + * Read a LEF "RECT" record from the file, and + * return a Rect in micron coordinates. + * + * Results: + * Returns a pointer to a Rect containing the micron + * coordinates, or NULL if an error occurred. + * + * Side Effects: + * Reads input from file f; + * + * Note: + * LEF/DEF does NOT define a RECT record as having (...) + * pairs, only routes. However, at least one DEF file + * contains this syntax, so it is checked. + * + *------------------------------------------------------------ + */ + +DSEG +LefReadRect(FILE *f, int curlayer, float oscale) +{ + char *token; + float llx, lly, urx, ury; + static struct dseg_ paintrect; + u_char needMatch = FALSE; + + token = LefNextToken(f, TRUE); + if (*token == '(') + { + token = LefNextToken(f, TRUE); + needMatch = TRUE; + } + if (!token || sscanf(token, "%f", &llx) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &lly) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (needMatch) + { + if (*token != ')') goto parse_error; + else token = LefNextToken(f, TRUE); + needMatch = FALSE; + } + if (*token == '(') + { + token = LefNextToken(f, TRUE); + needMatch = TRUE; + } + if (!token || sscanf(token, "%f", &urx) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &ury) != 1) goto parse_error; + if (needMatch) + { + token = LefNextToken(f, TRUE); + if (*token != ')') goto parse_error; + } + if (curlayer < 0) { + /* Issue warning but keep geometry with negative layer number */ + LefError(LEF_WARNING, "No layer defined for RECT.\n"); + } + + /* Scale coordinates (microns to centimicrons) */ + + paintrect.x1 = llx / oscale; + paintrect.y1 = lly / oscale; + paintrect.x2 = urx / oscale; + paintrect.y2 = ury / oscale; + paintrect.layer = curlayer; + return (&paintrect); + +parse_error: + LefError(LEF_ERROR, "Bad port geometry: RECT requires 4 values.\n"); + return (DSEG)NULL; +} + +/* + *------------------------------------------------------------ + * LefReadEnclosure -- + * + * Read a LEF "ENCLOSURE" record from the file, and + * return a Rect in micron coordinates, representing + * the bounding box of the stated enclosure dimensions + * in both directions, centered on the origin. + * + * Results: + * Returns a pointer to a Rect containing the micron + * coordinates, or NULL if an error occurred. + * + * Side Effects: + * Reads input from file f + * + *------------------------------------------------------------ + */ + +DSEG +LefReadEnclosure(FILE *f, int curlayer, float oscale) +{ + char *token; + float x, y, scale; + static struct dseg_ paintrect; + + token = LefNextToken(f, TRUE); + if (!token) goto enc_parse_error; + + if (sscanf(token, "%f", &x) != 1) { + if (!strcmp(token, "BELOW") || !strcmp(token, "ABOVE")) + /* NOTE: This creates two records but fails to differentiate */ + /* between the layers, both of which will be -1 if this is a */ + /* cut layer. Needs to be handled properly. */ + + token = LefNextToken(f, TRUE); + else + goto enc_parse_error; + } + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &y) != 1) goto enc_parse_error; + + if (curlayer < 0) { + /* Issue warning but keep geometry with negative layer number */ + LefError(LEF_ERROR, "No layer defined for RECT.\n"); + } + + /* Scale coordinates (microns to centimicrons) (doubled) */ + + scale = oscale / 2.0; + + paintrect.x1 = -x / scale; + paintrect.y1 = -y / scale; + paintrect.x2 = x / scale; + paintrect.y2 = y / scale; + paintrect.layer = curlayer; + return (&paintrect); + +enc_parse_error: + LefError(LEF_ERROR, "Bad enclosure geometry: ENCLOSURE requires 2 values.\n"); + return (DSEG)NULL; +} + +/* + *------------------------------------------------------------ + * Support routines for polygon reading + *------------------------------------------------------------ + */ + +#define HEDGE 0 /* Horizontal edge */ +#define REDGE 1 /* Rising edge */ +#define FEDGE -1 /* Falling edge */ + +/* + *------------------------------------------------------------ + * lefLowX --- + * + * Sort routine to find the lowest X coordinate between + * two DPOINT structures passed from qsort() + *------------------------------------------------------------ + */ + +int +lefLowX(DPOINT *a, DPOINT *b) +{ + DPOINT p = *a; + DPOINT q = *b; + + if (p->x < q->x) + return (-1); + if (p->x > q->x) + return (1); + return (0); +} + +/* + *------------------------------------------------------------ + * lefLowY --- + * + * Sort routine to find the lowest Y coordinate between + * two DPOINT structures passed from qsort() + *------------------------------------------------------------ + */ + +int +lefLowY(DPOINT *a, DPOINT *b) +{ + DPOINT p = *a; + DPOINT q = *b; + + if (p->y < q->y) + return (-1); + if (p->y > q->y) + return (1); + return (0); +} + +/* + *------------------------------------------------------------ + * lefOrient --- + * + * Assign a direction to each of the edges in a polygon. + * + * Note that edges have been sorted, but retain the original + * linked list pointers, from which we can determine the + * path orientation + * + *------------------------------------------------------------ + */ + +char +lefOrient(DPOINT *edges, int nedges, int *dir) +{ + int n; + DPOINT p, q; + + for (n = 0; n < nedges; n++) + { + p = edges[n]; + q = edges[n]->next; + + if (p->y == q->y) + { + dir[n] = HEDGE; + continue; + } + if (p->x == q->x) + { + if (p->y < q->y) + { + dir[n] = REDGE; + continue; + } + if (p->y > q->y) + { + dir[n] = FEDGE; + continue; + } + /* Point connects to itself */ + dir[n] = HEDGE; + continue; + } + /* It's not Manhattan, folks. */ + return (FALSE); + } + return (TRUE); +} + +/* + *------------------------------------------------------------ + * lefCross --- + * + * See if an edge crosses a particular area. + * Return TRUE if edge if vertical and if it crosses the + * y-range defined by ybot and ytop. Otherwise return + * FALSE. + *------------------------------------------------------------ + */ + +char +lefCross(DPOINT edge, int dir, double ybot, double ytop) +{ + double ebot, etop; + + switch (dir) + { + case REDGE: + ebot = edge->y; + etop = edge->next->y; + return (ebot <= ybot && etop >= ytop); + + case FEDGE: + ebot = edge->next->y; + etop = edge->y; + return (ebot <= ybot && etop >= ytop); + } + return (FALSE); +} + + +/* + *------------------------------------------------------------ + * LefPolygonToRects -- + * + * Convert Geometry information from a POLYGON statement + * into rectangles. NOTE: For now, this routine + * assumes that all points are Manhattan. It will flag + * non-Manhattan geometry + * + * the DSEG pointed to by rectListPtr is updated by + * having the list of rectangles appended to it. + * + *------------------------------------------------------------ + */ + +void +LefPolygonToRects(DSEG *rectListPtr, DPOINT pointlist) +{ + DPOINT ptail, p, *pts, *edges; + DSEG rtail, rex, new; + int npts = 0; + int *dir; + int curr, wrapno, n; + double xbot, xtop, ybot, ytop; + + if (pointlist == NULL) return; + + /* Close the path by duplicating 1st point if necessary */ + + for (ptail = pointlist; ptail->next; ptail = ptail->next); + + if ((ptail->x != pointlist->x) || (ptail->y != pointlist->y)) + { + p = (DPOINT)malloc(sizeof(struct dpoint_)); + p->x = pointlist->x; + p->y = pointlist->y; + p->layer = pointlist->layer; + p->next = NULL; + ptail->next = p; + } + + // To do: Break out non-manhattan parts here. + // See CIFMakeManhattanPath in magic-8.0 + + rex = NULL; + for (p = pointlist; p->next; p = p->next, npts++); + pts = (DPOINT *)malloc(npts * sizeof(DPOINT)); + edges = (DPOINT *)malloc(npts * sizeof(DPOINT)); + dir = (int *)malloc(npts * sizeof(int)); + npts = 0; + + for (p = pointlist; p->next; p = p->next, npts++) + { + // pts and edges are two lists of pointlist entries + // that are NOT linked lists and can be shuffled + // around by qsort(). The linked list "next" pointers + // *must* be retained. + + pts[npts] = p; + edges[npts] = p; + } + + if (npts < 4) + { + LefError(LEF_ERROR, "Polygon with fewer than 4 points.\n"); + goto done; + } + + /* Sort points by low y, edges by low x */ + qsort((char *)pts, npts, (int)sizeof(DPOINT), (__compar_fn_t)lefLowY); + qsort((char *)edges, npts, (int)sizeof(DPOINT), (__compar_fn_t)lefLowX); + + /* Find out which direction each edge points */ + + if (!lefOrient(edges, npts, dir)) + { + LefError(LEF_ERROR, "I can't handle non-manhattan polygons!\n"); + goto done; + } + + /* Scan the polygon from bottom to top. At each step, process + * a minimum-sized y-range of the polygon (i.e., a range such that + * there are no vertices inside the range). Use wrap numbers + * based on the edge orientations to determine how much of the + * x-range for this y-range should contain material. + */ + + for (curr = 1; curr < npts; curr++) + { + /* Find the next minimum-sized y-range. */ + + ybot = pts[curr - 1]->y; + while (ybot == pts[curr]->y) + if (++curr >= npts) goto done; + ytop = pts[curr]->y; + + /* Process all the edges that cross the y-range, from left + * to right. + */ + + for (wrapno = 0, n = 0; n < npts; n++) + { + if (wrapno == 0) xbot = edges[n]->x; + if (!lefCross(edges[n], dir[n], ybot, ytop)) + continue; + wrapno += (dir[n] == REDGE) ? 1 : -1; + if (wrapno == 0) + { + xtop = edges[n]->x; + if (xbot == xtop) continue; + new = (DSEG)malloc(sizeof(struct dseg_)); + new->x1 = xbot; + new->x2 = xtop; + new->y1 = ybot; + new->y2 = ytop; + new->layer = edges[n]->layer; + new->next = rex; + rex = new; + } + } + } + +done: + free(edges); + free(dir); + free(pts); + + if (*rectListPtr == NULL) + *rectListPtr = rex; + else + { + for (rtail = *rectListPtr; rtail->next; rtail = rtail->next); + rtail->next = rex; + } +} + +/* + *------------------------------------------------------------ + * LefReadPolygon -- + * + * Read Geometry information from a POLYGON statement + * + *------------------------------------------------------------ + */ + +DPOINT +LefReadPolygon(FILE *f, int curlayer, float oscale) +{ + DPOINT plist = NULL, newPoint; + char *token; + double px, py; + + while (1) + { + token = LefNextToken(f, TRUE); + if (token == NULL || *token == ';') break; + if (sscanf(token, "%lg", &px) != 1) + { + LefError(LEF_ERROR, "Bad X value in polygon.\n"); + LefEndStatement(f); + break; + } + + token = LefNextToken(f, TRUE); + if (token == NULL || *token == ';') + { + LefError(LEF_ERROR, "Missing Y value in polygon point!\n"); + break; + } + if (sscanf(token, "%lg", &py) != 1) + { + LefError(LEF_ERROR, "Bad Y value in polygon.\n"); + LefEndStatement(f); + break; + } + + newPoint = (DPOINT)malloc(sizeof(struct dpoint_)); + newPoint->x = px / (double)oscale; + newPoint->y = py / (double)oscale; + newPoint->layer = curlayer; + newPoint->next = plist; + plist = newPoint; + } + + return plist; +} + +/* + *------------------------------------------------------------ + * LefReadGeometry -- + * + * Read Geometry information from a LEF file. + * Used for PORT records and OBS statements. + * + * Results: + * Returns a linked list of all areas and types + * painted. + * + * Side Effects: + * Reads input from file f; + * Paints into the GATE lefMacro. + * + *------------------------------------------------------------ + */ + +enum lef_geometry_keys {LEF_LAYER = 0, LEF_WIDTH, LEF_PATH, + LEF_RECT, LEF_POLYGON, LEF_VIA, LEF_PORT_CLASS, + LEF_GEOMETRY_END}; + +DSEG +LefReadGeometry(GATE lefMacro, FILE *f, float oscale) +{ + int curlayer = -1, otherlayer = -1; + + char *token; + int keyword; + DSEG rectList = (DSEG)NULL; + DSEG paintrect, newRect; + DPOINT pointlist; + + static char *geometry_keys[] = { + "LAYER", + "WIDTH", + "PATH", + "RECT", + "POLYGON", + "VIA", + "CLASS", + "END", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, geometry_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_LAYER: + curlayer = LefReadLayers(f, FALSE, &otherlayer); + LefEndStatement(f); + break; + case LEF_WIDTH: + LefEndStatement(f); + break; + case LEF_PATH: + LefEndStatement(f); + break; + case LEF_RECT: + paintrect = (curlayer < 0) ? NULL : LefReadRect(f, curlayer, oscale); + if (paintrect) + { + /* Remember the area and layer */ + newRect = (DSEG)malloc(sizeof(struct dseg_)); + *newRect = *paintrect; + newRect->next = rectList; + rectList = newRect; + } + LefEndStatement(f); + break; + case LEF_POLYGON: + pointlist = LefReadPolygon(f, curlayer, oscale); + LefPolygonToRects(&rectList, pointlist); + break; + case LEF_VIA: + LefEndStatement(f); + break; + case LEF_PORT_CLASS: + LefEndStatement(f); + break; + case LEF_GEOMETRY_END: + if (!LefParseEndStatement(f, NULL)) + { + LefError(LEF_ERROR, "Geometry (PORT or OBS) END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_GEOMETRY_END) break; + } + return rectList; +} + +/* + *------------------------------------------------------------ + * LefReadPort -- + * + * A wrapper for LefReadGeometry, which adds a label + * to the last rectangle generated by the geometry + * parsing. + * + * Results: + * None. + * + * Side Effects: + * Reads input from file f; + * Paints into the GATE lefMacro. + * + *------------------------------------------------------------ + */ + +void +LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, pinArea, oscale) + GATE lefMacro; + FILE *f; + char *pinName; + int pinNum, pinDir, pinUse; + double pinArea; + float oscale; +{ + DSEG rectList, rlist; + BUS bus; + + rectList = LefReadGeometry(lefMacro, f, oscale); + + if (pinNum >= 0) { + int nodealloc, orignodes, ival; + char *aptr; + + if (lefMacro->nodes <= pinNum) { + orignodes = lefMacro->nodes; + lefMacro->nodes = (pinNum + 1); + nodealloc = lefMacro->nodes / 10; + if (nodealloc > (orignodes / 10)) { + nodealloc++; + lefMacro->taps = (DSEG *)realloc(lefMacro->taps, + nodealloc * 10 * sizeof(DSEG)); + lefMacro->noderec = (NODE *)realloc(lefMacro->noderec, + nodealloc * 10 * sizeof(NODE)); + lefMacro->direction = (u_char *)realloc(lefMacro->direction, + nodealloc * 10 * sizeof(u_char)); + lefMacro->area = (float *)realloc(lefMacro->area, + nodealloc * 10 * sizeof(float)); + lefMacro->use = (u_char *)realloc(lefMacro->use, + nodealloc * 10 * sizeof(u_char)); + lefMacro->netnum = (int *)realloc(lefMacro->netnum, + nodealloc * 10 * sizeof(int)); + lefMacro->node = (char **)realloc(lefMacro->node, + nodealloc * 10 * sizeof(char *)); + } + } + lefMacro->taps[pinNum] = rectList; + lefMacro->noderec[pinNum] = NULL; + lefMacro->area[pinNum] = 0.0; + lefMacro->direction[pinNum] = (u_char)pinDir; + lefMacro->area[pinNum] = pinArea; + lefMacro->use[pinNum] = (u_char)pinUse; + lefMacro->netnum[pinNum] = -1; + if (pinName != NULL) { + lefMacro->node[pinNum] = strdup(pinName); + + /* Check for bus delimiters */ + aptr = strrchr(pinName, delimiter); + if (aptr != NULL) { + if (sscanf(aptr + 1, "%d", &ival) == 1) { + *aptr = '\0'; + + /* Add to bus list if needed, or adjust bounds */ + for (bus = lefMacro->bus; bus; bus = bus->next) { + if (!strcmp(pinName, bus->busname)) { + if (ival > bus->high) bus->high = ival; + if (ival < bus->low) bus->low = ival; + break; + } + } + if (bus == NULL) { + bus = (BUS)malloc(sizeof(struct bus_)); + bus->busname = strdup(pinName); + bus->high = bus->low = ival; + bus->next = lefMacro->bus; + lefMacro->bus = bus; + } + *aptr = '['; + } + } + } + else + lefMacro->node[pinNum] = NULL; + } + else { + while (rectList) { + rlist = rectList->next; + free(rectList); + rectList = rlist; + } + } +} + +/* + *------------------------------------------------------------ + * LefReadPin -- + * + * Read a PIN statement from a LEF file. + * + * Results: + * 0 if the pin had a port (success), 1 if not (indicating + * an unused pin that should be ignored). + * + * Side Effects: + * Reads input from file f; + * Paints into the GATE lefMacro. + * + *------------------------------------------------------------ + */ + +enum lef_pin_keys {LEF_DIRECTION = 0, LEF_USE, LEF_PORT, LEF_CAPACITANCE, + LEF_ANTENNADIFF, LEF_ANTENNAGATE, LEF_ANTENNAMOD, + LEF_ANTENNAPAR, LEF_ANTENNAPARSIDE, LEF_ANTENNAMAX, LEF_ANTENNAMAXSIDE, + LEF_SHAPE, LEF_NETEXPR, LEF_PIN_END}; + +int +LefReadPin(lefMacro, f, pinname, pinNum, oscale) + GATE lefMacro; + FILE *f; + char *pinname; + int pinNum; + float oscale; +{ + char *token; + int keyword, subkey; + int pinDir = PORT_CLASS_DEFAULT; + int pinUse = PORT_USE_DEFAULT; + float pinArea = 0.0; + int retval = 1; + + static char *pin_keys[] = { + "DIRECTION", + "USE", + "PORT", + "CAPACITANCE", + "ANTENNADIFFAREA", + "ANTENNAGATEAREA", + "ANTENNAMODEL", + "ANTENNAPARTIALMETALAREA", + "ANTENNAPARTIALMETALSIDEAREA", + "ANTENNAMAXAREACAR", + "ANTENNAMAXSIDEAREACAR", + "SHAPE", + "NETEXPR", + "END", + NULL + }; + + static char *pin_classes[] = { + "DEFAULT", + "INPUT", + "OUTPUT", + "OUTPUT TRISTATE", + "INOUT", + "FEEDTHRU", + NULL + }; + + static int lef_class_to_bitmask[] = { + PORT_CLASS_DEFAULT, + PORT_CLASS_INPUT, + PORT_CLASS_OUTPUT, + PORT_CLASS_TRISTATE, + PORT_CLASS_BIDIRECTIONAL, + PORT_CLASS_FEEDTHROUGH + }; + + static char *pin_uses[] = { + "DEFAULT", + "SIGNAL", + "ANALOG", + "POWER", + "GROUND", + "CLOCK", + "TIEOFF", + "ANALOG", + "SCAN", + "RESET", + NULL + }; + + static int lef_use_to_bitmask[] = { + PORT_USE_DEFAULT, + PORT_USE_SIGNAL, + PORT_USE_ANALOG, + PORT_USE_POWER, + PORT_USE_GROUND, + PORT_USE_CLOCK + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, pin_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_DIRECTION: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, pin_classes); + if (subkey < 0) + LefError(LEF_ERROR, "Improper DIRECTION statement\n"); + else + pinDir = lef_class_to_bitmask[subkey]; + LefEndStatement(f); + break; + case LEF_USE: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, pin_uses); + if (subkey < 0) + LefError(LEF_ERROR, "Improper USE statement\n"); + else + pinUse = lef_use_to_bitmask[subkey]; + LefEndStatement(f); + break; + case LEF_PORT: + LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, pinArea, oscale); + retval = 0; + break; + case LEF_ANTENNAGATE: + /* Read off the next value as the pin's antenna gate area. */ + /* The layers or layers are not recorded. */ + token = LefNextToken(f, TRUE); + sscanf(token, "%g", &pinArea); + LefEndStatement(f); + break; + case LEF_CAPACITANCE: + case LEF_ANTENNADIFF: + case LEF_ANTENNAMOD: + case LEF_ANTENNAPAR: + case LEF_ANTENNAPARSIDE: + case LEF_ANTENNAMAX: + case LEF_ANTENNAMAXSIDE: + case LEF_NETEXPR: + case LEF_SHAPE: + LefEndStatement(f); /* Ignore. . . */ + break; + case LEF_PIN_END: + if (!LefParseEndStatement(f, pinname)) + { + LefError(LEF_ERROR, "Pin END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_PIN_END) break; + } + return retval; +} + +/* + *------------------------------------------------------------ + * LefEndStatement -- + * + * Read file input to EOF or a ';' token (end-of-statement) + * If we encounter a quote, make sure we don't terminate + * the statement on a semicolon that is part of the + * quoted material. + * + *------------------------------------------------------------ + */ + +void +LefEndStatement(FILE *f) +{ + char *token; + + while ((token = LefNextToken(f, TRUE)) != NULL) + if (*token == ';') break; +} + +/* + *------------------------------------------------------------ + * + * LefReadMacro -- + * + * Read in a MACRO section from a LEF file. + * + * Results: + * None. + * + * Side Effects: + * Creates a new cell definition in the database. + * + *------------------------------------------------------------ + */ + +enum lef_macro_keys {LEF_CLASS = 0, LEF_SIZE, LEF_ORIGIN, + LEF_SYMMETRY, LEF_SOURCE, LEF_SITE, LEF_PIN, LEF_OBS, + LEF_TIMING, LEF_FOREIGN, LEF_MACRO_END}; + +void +LefReadMacro(f, mname, oscale) + FILE *f; /* LEF file being read */ + char *mname; /* name of the macro */ + float oscale; /* scale factor to um, usually 1 */ +{ + GATE lefMacro, altMacro; + char *token, *pname, tsave[128]; + int keyword, pinNum, subkey; + float x, y; + u_char has_size, is_imported = FALSE; + struct dseg_ lefBBox; + + static char *macro_keys[] = { + "CLASS", + "SIZE", + "ORIGIN", + "SYMMETRY", + "SOURCE", + "SITE", + "PIN", + "OBS", + "TIMING", + "FOREIGN", + "END", + NULL + }; + + static char *macro_classes[] = { + "DEFAULT", + "CORE", + "BLOCK", + "PAD", + "RING", + "COVER", + "ENDCAP", + NULL + }; + + static int lef_macro_class_to_bitmask[] = { + MACRO_CLASS_DEFAULT, + MACRO_CLASS_CORE, + MACRO_CLASS_BLOCK, + MACRO_CLASS_PAD, + MACRO_CLASS_RING, + MACRO_CLASS_COVER, + MACRO_CLASS_ENDCAP + }; + + static char *macro_subclasses[] = { + ";", + "SPACER", + "ANTENNACELL", + "WELLTAP", + "TIEHIGH", + "TIELOW", + "FEEDTHRU" + }; + + static int lef_macro_subclass_to_bitmask[] = { + MACRO_SUBCLASS_NONE, + MACRO_SUBCLASS_SPACER, + MACRO_SUBCLASS_ANTENNA, + MACRO_SUBCLASS_WELLTAP, + MACRO_SUBCLASS_TIEHIGH, + MACRO_SUBCLASS_TIELOW, + MACRO_SUBCLASS_FEEDTHRU + }; + + /* Start by creating a new celldef */ + + lefMacro = (GATE)NULL; + for (altMacro = GateInfo; altMacro; altMacro = altMacro->next) + { + if (!strcmp(altMacro->gatename, mname)) { + lefMacro = altMacro; + break; + } + } + + while (lefMacro) + { + int suffix; + char newname[256]; + + altMacro = lefMacro; + for (suffix = 1; altMacro != NULL; suffix++) + { + sprintf(newname, "%250s_%d", mname, suffix); + for (altMacro = GateInfo; altMacro; altMacro = altMacro->next) + if (!strcmp(altMacro->gatename, newname)) + break; + } + LefError(LEF_WARNING, "Cell \"%s\" was already defined in this file. " + "Renaming original cell \"%s\"\n", mname, newname); + + lefMacro->gatename = strdup(newname); + lefMacro = lefFindCell(mname); + } + + // Create the new cell + lefMacro = (GATE)malloc(sizeof(struct gate_)); + lefMacro->gatename = strdup(mname); + lefMacro->gatetype = NULL; + lefMacro->gateclass = MACRO_CLASS_DEFAULT; + lefMacro->gatesubclass = MACRO_SUBCLASS_NONE; + lefMacro->width = 0.0; + lefMacro->height = 0.0; + lefMacro->placedX = 0.0; + lefMacro->placedY = 0.0; + lefMacro->obs = (DSEG)NULL; + lefMacro->next = GateInfo; + lefMacro->last = (GATE)NULL; + lefMacro->nodes = 0; + lefMacro->orient = 0; + // Allocate memory for up to 10 pins initially + lefMacro->taps = (DSEG *)malloc(10 * sizeof(DSEG)); + lefMacro->noderec = (NODE *)malloc(10 * sizeof(NODE)); + lefMacro->direction = (u_char *)malloc(10 * sizeof(u_char)); + lefMacro->area = (float *)malloc(10 * sizeof(float)); + lefMacro->use = (u_char *)malloc(10 * sizeof(u_char)); + lefMacro->netnum = (int *)malloc(10 * sizeof(int)); + lefMacro->node = (char **)malloc(10 * sizeof(char *)); + // Fill in 1st entry + lefMacro->taps[0] = NULL; + lefMacro->noderec[0] = NULL; + lefMacro->area[0] = 0.0; + lefMacro->node[0] = NULL; + lefMacro->bus = NULL; + lefMacro->netnum[0] = -1; + lefMacro->clientdata = (void *)NULL; + GateInfo = lefMacro; + + /* Set gate type to the site name for site definitions */ + pname = mname; + if (!strncmp(mname, "site_", 5)) pname += 5; + + /* Initial values */ + pinNum = 0; + has_size = FALSE; + lefBBox.x2 = lefBBox.x1 = 0.0; + lefBBox.y2 = lefBBox.y1 = 0.0; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, macro_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_CLASS: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, macro_classes); + if (subkey < 0) { + LefError(LEF_ERROR, "Improper macro CLASS statement\n"); + lefMacro->gateclass = MACRO_CLASS_DEFAULT; + } + else + lefMacro->gateclass = lef_macro_class_to_bitmask[subkey]; + token = LefNextToken(f, TRUE); + if (token) { + subkey = Lookup(token, macro_subclasses); + if (subkey < 0) { + lefMacro->gatesubclass = MACRO_SUBCLASS_NONE; + LefEndStatement(f); + } + else if (subkey > 0) { + lefMacro->gatesubclass = lef_macro_subclass_to_bitmask[subkey]; + LefEndStatement(f); + } + else + lefMacro->gatesubclass = MACRO_SUBCLASS_NONE; + } + else + LefEndStatement(f); + break; + case LEF_SIZE: + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &x) != 1) goto size_error; + token = LefNextToken(f, TRUE); /* skip keyword "BY" */ + if (!token) goto size_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &y) != 1) goto size_error; + + lefBBox.x2 = x + lefBBox.x1; + lefBBox.y2 = y + lefBBox.y1; + has_size = TRUE; + LefEndStatement(f); + break; +size_error: + LefError(LEF_ERROR, "Bad macro SIZE; requires values X BY Y.\n"); + LefEndStatement(f); + break; + case LEF_ORIGIN: + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &x) != 1) goto origin_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &y) != 1) goto origin_error; + + lefBBox.x1 = -x; + lefBBox.y1 = -y; + if (has_size) + { + lefBBox.x2 += lefBBox.x1; + lefBBox.y2 += lefBBox.y1; + } + LefEndStatement(f); + break; +origin_error: + LefError(LEF_ERROR, "Bad macro ORIGIN; requires 2 values.\n"); + LefEndStatement(f); + break; + case LEF_SYMMETRY: + token = LefNextToken(f, TRUE); + if (*token != '\n') + // DBPropPut(lefMacro, "LEFsymmetry", token + strlen(token) + 1); + ; + LefEndStatement(f); + break; + case LEF_SOURCE: + token = LefNextToken(f, TRUE); + if (*token != '\n') + // DBPropPut(lefMacro, "LEFsource", token); + ; + LefEndStatement(f); + break; + case LEF_SITE: + token = LefNextToken(f, TRUE); + if (*token != '\n') + // DBPropPut(lefMacro, "LEFsite", token); + ; + LefEndStatement(f); + break; + case LEF_PIN: + token = LefNextToken(f, TRUE); + /* Diagnostic */ + /* + fprintf(stdout, " Macro defines pin %s\n", token); + */ + sprintf(tsave, "%.127s", token); + if (is_imported) + LefSkipSection(f, tsave); + else + if (LefReadPin(lefMacro, f, tsave, pinNum, oscale) == 0) + pinNum++; + break; + case LEF_OBS: + /* Diagnostic */ + /* + fprintf(stdout, " Macro defines obstruction\n"); + */ + if (is_imported) + LefSkipSection(f, NULL); + else + lefMacro->obs = LefReadGeometry(lefMacro, f, oscale); + break; + case LEF_TIMING: + LefSkipSection(f, macro_keys[LEF_TIMING]); + break; + case LEF_FOREIGN: + LefEndStatement(f); + break; + case LEF_MACRO_END: + pname = mname; + if (!strncmp(mname, "site_", 5)) pname += 5; + if (!LefParseEndStatement(f, pname)) + { + LefError(LEF_ERROR, "Macro END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_MACRO_END) break; + } + + /* Finish up creating the cell */ + + if (lefMacro) { + if (has_size) { + lefMacro->width = (lefBBox.x2 - lefBBox.x1); + lefMacro->height = (lefBBox.y2 - lefBBox.y1); + + /* "placed" for macros (not instances) corresponds to the */ + /* cell origin. */ + + lefMacro->placedX = lefBBox.x1; + lefMacro->placedY = lefBBox.y1; + } + else { + LefError(LEF_ERROR, "Gate %s has no size information!\n", lefMacro->gatename); + } + } +} + +/* + *------------------------------------------------------------ + * + * LefAddViaGeometry -- + * + * Read in geometry for a VIA section from a LEF or DEF + * file. + * + * f LEF file being read + * lefl pointer to via info + * curlayer current tile type + * oscale output scaling + * + * Results: + * None. + * + * Side Effects: + * Adds to the lefLayer record for a via definition. + * + *------------------------------------------------------------ + */ + +void +LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale) +{ + DSEG currect; + DSEG viarect; + + /* Rectangles for vias are read in units of 1/2 lambda */ + currect = LefReadRect(f, curlayer, (oscale / 2)); + if (currect == NULL) return; + + /* First rect goes into info.via.area, others go into info.via.lr */ + if (lefl->info.via.area.layer < 0) + { + lefl->info.via.area = *currect; + + /* If entries exist for info.via.lr, this is a via GENERATE */ + /* statement, and metal enclosures have been parsed. Therefore */ + /* add the via dimensions to the enclosure rectangles. */ + + viarect = lefl->info.via.lr; + while (viarect != NULL) { + viarect->x1 += currect->x1; + viarect->x2 += currect->x2; + viarect->y1 += currect->y1; + viarect->y2 += currect->y2; + viarect = viarect->next; + } + } + else + { + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *currect; + viarect->next = lefl->info.via.lr; + lefl->info.via.lr = viarect; + } +} + +/* + *------------------------------------------------------------ + * + * LefNewRoute --- + * + * Allocate space for and fill out default records of a + * route layer. + * + *------------------------------------------------------------ + */ + +LefList LefNewRoute(char *name) +{ + LefList lefl; + + lefl = (LefList)malloc(sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_IGNORE; /* For starters */ + lefl->lefName = strdup(name); + + return lefl; +} + +/* + *------------------------------------------------------------ + * + * LefNewVia --- + * + * Allocate space for and fill out default records of a + * via definition. + * + *------------------------------------------------------------ + */ + +LefList LefNewVia(char *name) +{ + LefList lefl; + + lefl = (LefList)calloc(1, sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_VIA; + lefl->info.via.area.x1 = 0.0; + lefl->info.via.area.y1 = 0.0; + lefl->info.via.area.x2 = 0.0; + lefl->info.via.area.y2 = 0.0; + lefl->info.via.area.layer = -1; + lefl->info.via.cell = (GATE)NULL; + lefl->info.via.lr = (DSEG)NULL; + lefl->info.via.generated = FALSE; + lefl->info.via.respervia = 0.0; + lefl->info.via.spacing = (lefSpacingRule *)NULL; + if (name != NULL) + lefl->lefName = strdup(name); + else + lefl->lefName = NULL; + + return lefl; +} + +/* Note: Used directly below, as it is passed to variable "mode" + * in LefReadLayerSection(). However, mainly used in LefRead(). + */ + +enum lef_sections {LEF_VERSION = 0, + LEF_BUSBITCHARS, LEF_DIVIDERCHAR, LEF_MANUFACTURINGGRID, + LEF_USEMINSPACING, LEF_CLEARANCEMEASURE, + LEF_NAMESCASESENSITIVE, LEF_PROPERTYDEFS, LEF_UNITS, + LEF_SECTION_LAYER, LEF_SECTION_VIA, LEF_SECTION_VIARULE, + LEF_SECTION_SPACING, LEF_SECTION_SITE, LEF_PROPERTY, + LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP, + LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO, + LEF_END}; +/* + *------------------------------------------------------------ + * + * LefReadLayerSection -- + * + * Read in a LAYER, VIA, or VIARULE section from a LEF file. + * + * Results: + * None. + * + * Side Effects: + * + *------------------------------------------------------------ + */ + +enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH, + LEF_LAYER_MINWIDTH, LEF_LAYER_MAXWIDTH, LEF_LAYER_AREA, + LEF_LAYER_SPACING, LEF_LAYER_SPACINGTABLE, + LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET, + LEF_LAYER_WIREEXT, + LEF_LAYER_RES, LEF_LAYER_CAP, LEF_LAYER_EDGECAP, + LEF_LAYER_THICKNESS, LEF_LAYER_HEIGHT, + LEF_LAYER_MINDENSITY, LEF_LAYER_ANTENNA, + LEF_LAYER_ANTENNADIFF, LEF_LAYER_ANTENNASIDE, + LEF_LAYER_AGG_ANTENNA, LEF_LAYER_AGG_ANTENNADIFF, + LEF_LAYER_AGG_ANTENNASIDE, + LEF_LAYER_ACCURRENT, LEF_LAYER_DCCURRENT, + LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT, + LEF_VIA_ENCLOSURE, LEF_VIA_PREFERENCLOSURE, + LEF_VIARULE_OVERHANG, + LEF_VIARULE_METALOVERHANG, LEF_VIARULE_VIA, + LEF_VIARULE_GENERATE, LEF_LAYER_END}; + +enum lef_spacing_keys {LEF_SPACING_RANGE=0, LEF_SPACING_BY, + LEF_SPACING_ADJACENTCUTS, LEF_END_LAYER_SPACING}; + +void +LefReadLayerSection(f, lname, mode, lefl) + FILE *f; /* LEF file being read */ + char *lname; /* name of the layer */ + int mode; /* layer, via, or viarule */ + LefList lefl; /* pointer to layer info */ +{ + char *token, *tp; + int keyword, typekey = -1, entries, i; + int curlayer = -1; + double dvalue, oscale; + LefList altVia; + lefSpacingRule *newrule = NULL, *testrule; + + /* These are defined in the order of CLASS_* in lefInt.h */ + static char *layer_type_keys[] = { + "ROUTING", + "CUT", + "MASTERSLICE", + "OVERLAP", + NULL + }; + + static char *layer_keys[] = { + "TYPE", + "WIDTH", + "MINWIDTH", + "MAXWIDTH", + "AREA", + "SPACING", + "SPACINGTABLE", + "PITCH", + "DIRECTION", + "OFFSET", + "WIREEXTENSION", + "RESISTANCE", + "CAPACITANCE", + "EDGECAPACITANCE", + "THICKNESS", + "HEIGHT", + "MINIMUMDENSITY", + "ANTENNAAREARATIO", + "ANTENNADIFFAREARATIO", + "ANTENNASIDEAREARATIO", + "ANTENNACUMAREARATIO", + "ANTENNACUMDIFFAREARATIO", + "ANTENNACUMSIDEAREARATIO", + "ACCURRENTDENSITY", + "DCCURRENTDENSITY", + "DEFAULT", + "LAYER", + "RECT", + "ENCLOSURE", + "PREFERENCLOSURE", + "OVERHANG", + "METALOVERHANG", + "VIA", + "GENERATE", + "END", + NULL + }; + + static char *spacing_keys[] = { + "RANGE", + "BY", + "ADJACENTCUTS", + ";", + NULL + }; + + /* Database is assumed to be in microns. */ + /* If not, we need to parse the UNITS record, which is */ + /* currently ignored. */ + + oscale = 1; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, layer_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_LAYER_TYPE: + token = LefNextToken(f, TRUE); + if (*token != '\n') + { + typekey = Lookup(token, layer_type_keys); + if (typekey < 0) + LefError(LEF_WARNING, "Unknown layer type \"%s\" in LEF file; " + "ignoring.\n", token); + } + if (lefl->lefClass == CLASS_IGNORE) { + lefl->lefClass = typekey; + if (typekey == CLASS_ROUTE) { + + lefl->info.route.width = 0.0; + lefl->info.route.spacing = NULL; + lefl->info.route.pitchx = 0.0; + lefl->info.route.pitchy = 0.0; + // Use -1.0 as an indication that offset has not + // been specified and needs to be set to default. + lefl->info.route.offsetx = -1.0; + lefl->info.route.offsety = -1.0; + lefl->info.route.hdirection = DIR_UNKNOWN; + + lefl->info.route.minarea = 0.0; + lefl->info.route.thick = 0.0; + lefl->info.route.antenna = 0.0; + lefl->info.route.method = CALC_NONE; + + lefl->info.route.areacap = 0.0; + lefl->info.route.respersq = 0.0; + lefl->info.route.edgecap = 0.0; + + /* A routing type has been declared. Assume */ + /* this takes the name "metal1", "M1", or some */ + /* variant thereof. */ + + for (tp = lefl->lefName; *tp != '\0'; tp++) { + if (*tp >= '0' && *tp <= '9') { + sscanf(tp, "%d", &lefl->type); + + /* "metal1", e.g., is assumed to be layer #0 */ + /* This may not be a proper assumption, always */ + + lefl->type--; + break; + } + } + + /* This is probably some special non-numerical */ + /* name for a top metal layer. Take a stab at */ + /* it, defining it to be the next layer up from */ + /* whatever the previous topmost route layer */ + /* was. This should work unless the LEF file */ + /* is incorrectly written. */ + + if (lefl->type < 0) { + lefl->type = LefGetMaxRouteLayer(); + } + } + else if (typekey == CLASS_CUT || typekey == CLASS_VIA) { + lefl->info.via.spacing = NULL; + lefl->info.via.area.x1 = 0.0; + lefl->info.via.area.y1 = 0.0; + lefl->info.via.area.x2 = 0.0; + lefl->info.via.area.y2 = 0.0; + lefl->info.via.area.layer = -1; + lefl->info.via.cell = (GATE)NULL; + lefl->info.via.lr = (DSEG)NULL; + + /* Note: lefl->type not set here for cut */ + /* layers so that route layers will all be */ + /* clustered at the bottom. */ + } + } + else if (lefl->lefClass != typekey) { + LefError(LEF_ERROR, "Attempt to reclassify layer %s from %s to %s\n", + lname, layer_type_keys[lefl->lefClass], + layer_type_keys[typekey]); + } + LefEndStatement(f); + break; + case LEF_LAYER_ACCURRENT: + // Not used, but syntax is f**king stupid and has to be + // specially handled, or it breaks the parser. + token = LefNextToken(f, TRUE); /* "AVERAGE", ... */ + token = LefNextToken(f, TRUE); /* Value or "FREQUENCY" */ + LefEndStatement(f); + if (!strcmp(token, "FREQUENCY")) + { + while (TRUE) { + token = LefNextToken(f, TRUE); + LefEndStatement(f); + if (!strcmp(token, "TABLEENTRIES")) { + break; + } + } + } + break; + case LEF_LAYER_DCCURRENT: + // Not used. See comments above for ACCURRENTDENSITY + token = LefNextToken(f, TRUE); /* "AVERAGE" */ + token = LefNextToken(f, TRUE); /* Value or "WIDTH" */ + LefEndStatement(f); + if (!strcmp(token, "WIDTH")) + { + while (TRUE) { + token = LefNextToken(f, TRUE); + LefEndStatement(f); + if (!strcmp(token, "TABLEENTRIES")) { + break; + } + } + } + break; + case LEF_LAYER_MINWIDTH: + // Not handled if width is already defined. + if ((lefl->lefClass != CLASS_ROUTE) || + (lefl->info.route.width != 0)) { + LefEndStatement(f); + break; + } + /* drop through */ + case LEF_LAYER_WIDTH: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + if (lefl->lefClass == CLASS_ROUTE) + lefl->info.route.width = dvalue / (double)oscale; + else if (lefl->lefClass == CLASS_CUT) { + double baseval = (dvalue / (double)oscale) / 2.0; + lefl->info.via.area.x1 = -baseval; + lefl->info.via.area.y1 = -baseval; + lefl->info.via.area.x2 = baseval; + lefl->info.via.area.y2 = baseval; + } + LefEndStatement(f); + break; + case LEF_LAYER_MAXWIDTH: + // Not handled. + LefEndStatement(f); + break; + case LEF_LAYER_AREA: + /* Read minimum area rule value */ + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units of area (length * length) + lefl->info.route.minarea = dvalue / (double)oscale / (double)oscale; + } + LefEndStatement(f); + break; + case LEF_LAYER_SPACING: + if ((lefl->lefClass != CLASS_ROUTE) && (lefl->lefClass != CLASS_CUT) && + (lefl->lefClass != CLASS_VIA)) { + LefEndStatement(f); + break; + } + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + token = LefNextToken(f, TRUE); + typekey = Lookup(token, spacing_keys); + + newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); + + // If no range specified, then the rule goes in front + if (typekey == LEF_SPACING_RANGE) { + // Get range minimum, ignore range maximum, and sort + // the spacing order. + newrule->spacing = dvalue / (double)oscale; + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + newrule->width = dvalue / (double)oscale; + for (testrule = lefl->info.route.spacing; testrule; + testrule = testrule->next) + if (testrule->next == NULL || testrule->next->width > + newrule->width) + break; + + if (!testrule) { + newrule->next = NULL; + lefl->info.route.spacing = newrule; + } + else { + newrule->next = testrule->next; + testrule->next = newrule; + } + token = LefNextToken(f, TRUE); + typekey = Lookup(token, spacing_keys); + } + else if (typekey == LEF_SPACING_BY) { + /* In info.via.spacing, save two rules; first one */ + /* is for X spacing, second is for Y spacing */ + + newrule->spacing = dvalue / (double)oscale; + newrule->width = 0; + newrule->next = NULL; + lefl->info.via.spacing = newrule; + + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); + newrule->spacing = dvalue / (double)oscale; + newrule->width = 0; + newrule->next = NULL; + lefl->info.via.spacing->next = newrule; + } + else if (typekey == LEF_SPACING_ADJACENTCUTS) { + /* ADJACENTCUTS not handled here (yet) */ + /* Need this for power post generation in addspacers */ + /* (Do nothing here) */ + } + else if (typekey >= 0) { + newrule->spacing = dvalue / (double)oscale; + newrule->width = 0.0; + newrule->next = lefl->info.route.spacing; + lefl->info.route.spacing = newrule; + } + if (typekey != LEF_END_LAYER_SPACING) + LefEndStatement(f); + break; + + case LEF_LAYER_SPACINGTABLE: + // Use the values for the maximum parallel runlength + token = LefNextToken(f, TRUE); // "PARALLELRUNLENTTH" + entries = 0; + while (1) { + token = LefNextToken(f, TRUE); + if (*token == ';' || !strcmp(token, "WIDTH")) + break; + else + entries++; + } + if (*token != ';') + newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); + + while (*token != ';') { + token = LefNextToken(f, TRUE); // Minimum width value + sscanf(token, "%lg", &dvalue); + newrule->width = dvalue / (double)oscale; + + for (i = 0; i < entries; i++) { + token = LefNextToken(f, TRUE); // Spacing value + } + sscanf(token, "%lg", &dvalue); + newrule->spacing = dvalue / (double)oscale; + token = LefNextToken(f, TRUE); + + for (testrule = lefl->info.route.spacing; testrule; + testrule = testrule->next) + if (testrule->next == NULL || testrule->next->width > + newrule->width) + break; + + if (!testrule) { + newrule->next = NULL; + lefl->info.route.spacing = newrule; + } + else { + newrule->next = testrule->next; + testrule->next = newrule; + } + token = LefNextToken(f, TRUE); + if (strcmp(token, "WIDTH")) break; + } + break; + case LEF_LAYER_PITCH: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + lefl->info.route.pitchx = dvalue / (double)oscale; + + token = LefNextToken(f, TRUE); + if (token && (*token != ';')) { + sscanf(token, "%lg", &dvalue); + lefl->info.route.pitchy = dvalue / (double)oscale; + LefEndStatement(f); + } + else { + lefl->info.route.pitchy = lefl->info.route.pitchx; + /* If the orientation is known, then zero the pitch */ + /* in the opposing direction. If not, then set the */ + /* direction to DIR_RESOLVE so that the pitch in */ + /* the opposing direction can be zeroed when the */ + /* direction is specified. */ + + if (lefl->info.route.hdirection == DIR_UNKNOWN) + lefl->info.route.hdirection = DIR_RESOLVE; + else if (lefl->info.route.hdirection == DIR_VERTICAL) + lefl->info.route.pitchy = 0.0; + else if (lefl->info.route.hdirection == DIR_HORIZONTAL) + lefl->info.route.pitchx = 0.0; + } + + /* Offset default is 1/2 the pitch. Offset is */ + /* intialized to -1 to tell whether or not the value */ + /* has been set by an OFFSET statement. */ + if (lefl->info.route.offsetx < 0.0) + lefl->info.route.offsetx = lefl->info.route.pitchx / 2.0; + if (lefl->info.route.offsety < 0.0) + lefl->info.route.offsety = lefl->info.route.pitchy / 2.0; + break; + case LEF_LAYER_DIRECTION: + token = LefNextToken(f, TRUE); + LefLower(token); + if (lefl->info.route.hdirection == DIR_RESOLVE) { + if (token[0] == 'h') + lefl->info.route.pitchx = 0.0; + else if (token[0] == 'v') + lefl->info.route.pitchy = 0.0; + } + lefl->info.route.hdirection = (token[0] == 'h') ? DIR_HORIZONTAL + : DIR_VERTICAL; + LefEndStatement(f); + break; + case LEF_LAYER_OFFSET: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + lefl->info.route.offsetx = dvalue / (double)oscale; + + token = LefNextToken(f, TRUE); + if (token && (*token != ';')) { + sscanf(token, "%lg", &dvalue); + lefl->info.route.offsety = dvalue / (double)oscale; + LefEndStatement(f); + } + else { + lefl->info.route.offsety = lefl->info.route.offsetx; + } + break; + case LEF_LAYER_RES: + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + if (!strcmp(token, "RPERSQ")) { + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + // Units are ohms per square + lefl->info.route.respersq = dvalue; + } + } + else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT) { + sscanf(token, "%lg", &dvalue); + lefl->info.via.respervia = dvalue; // Units ohms + } + LefEndStatement(f); + break; + case LEF_LAYER_CAP: + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + if (!strcmp(token, "CPERSQDIST")) { + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + // Units are pF per squared unit length + lefl->info.route.areacap = dvalue / + ((double)oscale * (double)oscale); + } + } + LefEndStatement(f); + break; + case LEF_LAYER_EDGECAP: + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units are pF per unit length + lefl->info.route.edgecap = dvalue / (double)oscale; + } + LefEndStatement(f); + break; + case LEF_LAYER_THICKNESS: + case LEF_LAYER_HEIGHT: + /* Assuming thickness and height are the same thing? */ + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units of length + lefl->info.route.thick = dvalue / (double)oscale; + } + LefEndStatement(f); + break; + case LEF_LAYER_ANTENNA: + case LEF_LAYER_ANTENNASIDE: + case LEF_LAYER_AGG_ANTENNA: + case LEF_LAYER_AGG_ANTENNASIDE: + /* NOTE: Assuming that only one of these methods will */ + /* be used! If more than one is present, then only the */ + /* last one will be recorded and used. */ + + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Unitless values (ratio) + lefl->info.route.antenna = dvalue; + } + if (keyword == LEF_LAYER_ANTENNA) + lefl->info.route.method = CALC_AREA; + else if (keyword == LEF_LAYER_ANTENNASIDE) + lefl->info.route.method = CALC_SIDEAREA; + else if (keyword == LEF_LAYER_AGG_ANTENNA) + lefl->info.route.method = CALC_AGG_AREA; + else + lefl->info.route.method = CALC_AGG_SIDEAREA; + LefEndStatement(f); + break; + case LEF_LAYER_ANTENNADIFF: + case LEF_LAYER_AGG_ANTENNADIFF: + /* Not specifically handling these antenna types */ + /* (antenna ratios for antennas connected to diodes, */ + /* which can still blow gates if the diode area is */ + /* insufficiently large.) */ + LefEndStatement(f); + break; + case LEF_LAYER_MINDENSITY: + case LEF_LAYER_WIREEXT: + /* Not specifically handling these */ + LefEndStatement(f); + break; + case LEF_VIA_DEFAULT: + case LEF_VIARULE_GENERATE: + /* Do nothing; especially, don't look for end-of-statement! */ + break; + case LEF_VIA_LAYER: + curlayer = LefReadLayer(f, FALSE); + LefEndStatement(f); + break; + case LEF_VIA_RECT: + if (curlayer >= 0) + LefAddViaGeometry(f, lefl, curlayer, oscale); + LefEndStatement(f); + break; + case LEF_VIA_ENCLOSURE: + /* Defines how to draw via metal layers. */ + /* Note that values can interact with ENCLOSURE */ + /* values given for the cut layer type. This is */ + /* not being handled. */ + { + DSEG viarect, encrect; + encrect = LefReadEnclosure(f, curlayer, oscale); + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *encrect; + viarect->next = lefl->info.via.lr; + lefl->info.via.lr = viarect; + } + if (mode == LEF_SECTION_VIARULE) + lefl->info.via.generated = TRUE; + LefEndStatement(f); + break; + case LEF_VIARULE_OVERHANG: + case LEF_VIARULE_METALOVERHANG: + /* These are from older LEF definitions (e.g., 5.4) */ + /* and cannot completely specify via geometry. So if */ + /* seen, ignore the rest of the via section. Only the */ + /* explicitly defined VIA types will be used. */ + LefError(LEF_WARNING, "NOTE: Old format VIARULE ignored.\n"); + lefl->lefClass == CLASS_IGNORE; + LefEndStatement(f); + /* LefSkipSection(f, lname); */ /* Continue parsing */ + break; + case LEF_VIA_PREFERENCLOSURE: + /* Ignoring this. */ + LefEndStatement(f); + break; + case LEF_VIARULE_VIA: + LefEndStatement(f); + break; + case LEF_LAYER_END: + if (!LefParseEndStatement(f, lname)) + { + LefError(LEF_ERROR, "Layer END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_LAYER_END) break; + } +} + +/* + *------------------------------------------------------------ + * + * LefRead -- + * + * Read a .lef file and generate all routing configuration + * structures and values from the LAYER, VIA, and MACRO sections + * + * Results: + * None. + * + * Side Effects: + * Many. Cell definitions are created and added to + * the GateInfo database. + * + *------------------------------------------------------------ + */ + +/* See above for "enum lef_sections {...}" */ + +int +LefRead(inName) + char *inName; +{ + FILE *f; + char filename[256]; + char *token; + char tsave[128]; + int keyword, layer; + int oprecis = 100; // = 1 / manufacturing grid (microns) + float oscale; + double xydiff, ogrid, minwidth; + LefList lefl; + DSEG grect; + GATE gateginfo; + + static char *sections[] = { + "VERSION", + "BUSBITCHARS", + "DIVIDERCHAR", + "MANUFACTURINGGRID", + "USEMINSPACING", + "CLEARANCEMEASURE", + "NAMESCASESENSITIVE", + "PROPERTYDEFINITIONS", + "UNITS", + "LAYER", + "VIA", + "VIARULE", + "SPACING", + "SITE", + "PROPERTY", + "NOISETABLE", + "CORRECTIONTABLE", + "IRDROP", + "ARRAY", + "TIMING", + "BEGINEXT", + "MACRO", + "END", + NULL + }; + + if (!strrchr(inName, '.')) + sprintf(filename, "%s.lef", inName); + else + strcpy(filename, inName); + + f = fopen(filename, "r"); + + if (f == NULL) + { + fprintf(stderr, "Cannot open input file: "); + perror(filename); + return 0; + } + + if (Verbose > 0) { + fprintf(stdout, "Reading LEF data from file %s.\n", filename); + fflush(stdout); + } + + oscale = 1; + + LefHashInit(); + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, sections); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_VERSION: + case LEF_DIVIDERCHAR: + case LEF_CLEARANCEMEASURE: + case LEF_USEMINSPACING: + case LEF_NAMESCASESENSITIVE: + LefEndStatement(f); + break; + case LEF_BUSBITCHARS: + token = LefNextToken(f, TRUE); + delimiter = (*token == '\"') ? *(token + 1) : *token; + LefEndStatement(f); + break; + case LEF_MANUFACTURINGGRID: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%lg", &ogrid) == 1) + oprecis = (int)((1.0 / ogrid) + 0.5); + LefEndStatement(f); + break; + case LEF_PROPERTYDEFS: + LefSkipSection(f, sections[LEF_PROPERTYDEFS]); + break; + case LEF_UNITS: + LefSkipSection(f, sections[LEF_UNITS]); + break; + + case LEF_SECTION_VIA: + case LEF_SECTION_VIARULE: + token = LefNextToken(f, TRUE); + sprintf(tsave, "%.127s", token); + + lefl = LefFindLayer(token); + + if (keyword == LEF_SECTION_VIARULE) { + char *vianame = (char *)malloc(strlen(token) + 3); + sprintf(vianame, "%s_0", token); + + /* If the following keyword is GENERATE, then */ + /* prepare up to four contact types to represent */ + /* all possible orientations of top and bottom */ + /* metal layers. If no GENERATE keyword, ignore. */ + + token = LefNextToken(f, TRUE); + if (!strcmp(token, "GENERATE")) { + lefl = LefNewVia(vianame); + lefl->next = LefInfo; + LefInfo = lefl; + LefReadLayerSection(f, tsave, keyword, lefl); + } + else { + LefSkipSection(f, tsave); + } + free(vianame); + } + else if (lefl == NULL) + { + lefl = LefNewVia(token); + lefl->next = LefInfo; + LefInfo = lefl; + LefReadLayerSection(f, tsave, keyword, lefl); + } + else + { + LefError(LEF_WARNING, "Warning: Cut type \"%s\" redefined.\n", + token); + lefl = LefRedefined(lefl, token); + LefReadLayerSection(f, tsave, keyword, lefl); + } + break; + + case LEF_SECTION_LAYER: + token = LefNextToken(f, TRUE); + sprintf(tsave, "%.127s", token); + + lefl = LefFindLayer(token); + if (lefl == (LefList)NULL) + { + lefl = LefNewRoute(token); + lefl->next = LefInfo; + LefInfo = lefl; + } + else + { + if (lefl && lefl->type < 0) + { + LefError(LEF_ERROR, "Layer %s is only defined for" + " obstructions!\n", token); + LefSkipSection(f, tsave); + break; + } + } + LefReadLayerSection(f, tsave, keyword, lefl); + break; + + case LEF_SECTION_SPACING: + LefSkipSection(f, sections[LEF_SECTION_SPACING]); + break; + case LEF_SECTION_SITE: + token = LefNextToken(f, TRUE); + if (Verbose > 0) + fprintf(stdout, "LEF file: Defines site %s\n", token); + sprintf(tsave, "site_%.122s", token); + LefReadMacro(f, tsave, oscale); + break; + case LEF_PROPERTY: + LefSkipSection(f, NULL); + break; + case LEF_NOISETABLE: + LefSkipSection(f, sections[LEF_NOISETABLE]); + break; + case LEF_CORRECTIONTABLE: + LefSkipSection(f, sections[LEF_CORRECTIONTABLE]); + break; + case LEF_IRDROP: + LefSkipSection(f, sections[LEF_IRDROP]); + break; + case LEF_ARRAY: + LefSkipSection(f, sections[LEF_ARRAY]); + break; + case LEF_SECTION_TIMING: + LefSkipSection(f, sections[LEF_SECTION_TIMING]); + break; + case LEF_EXTENSION: + LefSkipSection(f, sections[LEF_EXTENSION]); + break; + case LEF_MACRO: + token = LefNextToken(f, TRUE); + /* Diagnostic */ + /* + fprintf(stdout, "LEF file: Defines new cell %s\n", token); + */ + sprintf(tsave, "%.127s", token); + LefReadMacro(f, tsave, oscale); + break; + case LEF_END: + if (!LefParseEndStatement(f, "LIBRARY")) + { + LefError(LEF_ERROR, "END statement out of context.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_END) break; + } + if (Verbose > 0) { + fprintf(stdout, "LEF read: Processed %d lines.\n", lefCurrentLine); + LefError(LEF_ERROR, NULL); /* print statement of errors, if any */ + } + + /* Cleanup */ + if (f != NULL) fclose(f); + + /* Make sure that the gate list has one entry called "pin" */ + + for (gateginfo = GateInfo; gateginfo; gateginfo = gateginfo->next) + if (!strcasecmp(gateginfo->gatename, "pin")) + break; + + if (!gateginfo) { + /* Add a new GateInfo entry for pseudo-gate "pin" */ + gateginfo = (GATE)malloc(sizeof(struct gate_)); + gateginfo->gatetype = NULL; + gateginfo->gateclass = MACRO_CLASS_DEFAULT; + gateginfo->gatesubclass = MACRO_SUBCLASS_NONE; + gateginfo->gatename = (char *)malloc(4); + strcpy(gateginfo->gatename, "pin"); + gateginfo->width = 0.0; + gateginfo->height = 0.0; + gateginfo->placedX = 0.0; + gateginfo->placedY = 0.0; + gateginfo->nodes = 1; + + gateginfo->taps = (DSEG *)malloc(sizeof(DSEG)); + gateginfo->noderec = (NODE *)malloc(sizeof(NODE)); + gateginfo->area = (float *)malloc(sizeof(float)); + gateginfo->direction = (u_char *)malloc(sizeof(u_char)); + gateginfo->use = (u_char *)malloc(sizeof(u_char)); + gateginfo->netnum = (int *)malloc(sizeof(int)); + gateginfo->node = (char **)malloc(sizeof(char *)); + + grect = (DSEG)malloc(sizeof(struct dseg_)); + grect->x1 = grect->x2 = 0.0; + grect->y1 = grect->y2 = 0.0; + grect->next = (DSEG)NULL; + gateginfo->obs = (DSEG)NULL; + gateginfo->next = GateInfo; + gateginfo->last = (GATE)NULL; + gateginfo->taps[0] = grect; + gateginfo->noderec[0] = NULL; + gateginfo->area[0] = 0.0; + gateginfo->netnum[0] = -1; + gateginfo->node[0] = strdup("pin"); + gateginfo->clientdata = (void *)NULL; + GateInfo = gateginfo; + + LefHashMacro(gateginfo); + } + return oprecis; +} |