diff options
author | Tim Edwards <tim@opencircuitdesign.com> | 2014-09-16 10:20:18 -0400 |
---|---|---|
committer | Tim Edwards <tim@opencircuitdesign.com> | 2014-09-16 10:20:18 -0400 |
commit | 5dc5691ea246a85d546f3c1ff01fd13279731bd2 (patch) | |
tree | 233bf593f254f5525eaa4249d094b6dac4440004 /lef.c |
Initial commit at Tue Sep 16 10:20:18 EDT 2014 by tim on stravinsky.tim.linglan.net
Diffstat (limited to 'lef.c')
-rw-r--r-- | lef.c | 2518 |
1 files changed, 2518 insertions, 0 deletions
@@ -0,0 +1,2518 @@ +/* + * 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. + * + * It is assumed that the "route.cfg" file has been called prior to this, and + * so the basic linked lists have been created. The contents of the LEF file + * will override anything in the route.cfg file, allowing the route.cfg file + * to contain a minimum of information, but also allowing for the possibility + * that there is no LEF file for the technology, and that all such information + * is in the route.cfg file. + */ + +#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 "qrouter.h" +#include "node.h" +#include "qconfig.h" +#include "maze.h" +#include "lef.h" + +/* ---------------------------------------------------------------------*/ + +/* Current line number for reading */ +int lefCurrentLine; + +/* Information about routing layers */ +LefList LefInfo; + +/* Gate information is in the linked list GateInfo, imported */ + +/*--------------------------------------------------------- + * 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. + * + * Results: + * None. + * + * Side Effects: + * Prints to the output (stderr). + * + *------------------------------------------------------------ + */ + +void +LefError(char *fmt, ...) +{ + static int errors = 0; + va_list args; + + if (Verbose == 0) return; + + if (fmt == NULL) /* Special case: report any errors and reset */ + { + if (errors) + { + Fprintf(stdout, "LEF Read: encountered %d error%s total.\n", + errors, (errors == 1) ? "" : "s"); + errors = 0; + } + return; + } + + if (errors < LEF_MAX_ERRORS) + { + Fprintf(stderr, "LEF Read, Line %d: ", lefCurrentLine); + va_start(args, fmt); + Vprintf(stderr, fmt, args); + va_end(args); + Flush(stderr); + } + else if (errors == LEF_MAX_ERRORS) + Fprintf(stderr, "LEF Read: Further errors will not be reported.\n"); + + errors++; +} + +/* + *------------------------------------------------------------ + * + * 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, words; + char *match_name[2]; + + match_name[0] = match; + match_name[1] = NULL; + + token = LefNextToken(f, (match == NULL) ? FALSE : TRUE); + if (token == NULL) + { + LefError("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("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 + * list. + * + *------------------------------------------------------------ + */ + +GATE +lefFindCell(char *name) +{ + GATE gateginfo; + + for (gateginfo = GateInfo; gateginfo; gateginfo = gateginfo->next) { + if (!strcasecmp(gateginfo->gatename, name)) + return gateginfo; + } + return (GATE)NULL; +} + +/* + *------------------------------------------------------------ + * + * 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; +} + +/* + *------------------------------------------------------------ + * LefRedefined -- + * + * In preparation for redefining a LEF layer, we need + * to first check if there are multiple names associated + * with the lefLayer entry. If so, split the entry into + * two copies, so that the redefinition doesn't affect + * the other LEF names. + * + * Results: + * Pointer to a lefLayer, which may or may not be the + * same one presented to the subroutine. + * + * Side Effects: + * May add an entry to the list of LEF layers. + * + *------------------------------------------------------------ + */ + +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 routing layer number defined by the LEF file + *--------------------------------------------------------------- + */ + +int +LefGetMaxLayer() +{ + int maxlayer = -1; + LefList lefl; + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + 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; + double dist; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.width / 2.0 + + lefl->info.route.spacing->spacing; + } + } + return MIN(PitchX[layer], PitchY[layer]) - PathWidth[layer] / 2.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 MIN(PitchX[layer], PitchY[layer]) / 2.0; +} + +/* + *------------------------------------------------------------ + * 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; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.offset; + } + } + return MIN(PitchX[layer], PitchY[layer]) / 2.0; +} + +/* + *------------------------------------------------------------ + * Determine and return the width of a via. The first layer + * is the base (lower) layer of the via (e.g., layer 0, or + * metal1, for via12). The second layer is the layer for + * which we want the width rule (e.g., 0 or 1, for metal1 + * or metal2). If dir = 0, return the side-to-side width, + * otherwise, return the top-to-bottom width. This accounts + * for non-square vias. + * + * Note that Via rectangles are stored with x2 dimensions + * because the center can be on a half-grid position; so, + * return half the value obtained. + * + * To-do: Differentiate between X and Y vias when doing + * checkerboard via patterning. + *------------------------------------------------------------ + */ + +double +LefGetViaWidth(int base, int layer, int dir) +{ + DSEG lrect; + LefList lefl; + double width; + + lefl = LefFindLayer(ViaX[base]); + if (!lefl) { + if (base == Num_layers) + lefl = LefFindLayer(ViaX[base - 1]); + } + if (lefl) { + 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; + return width / 2.0; + } + 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; + return width / 2.0; + } + } + } + } + return MIN(PitchX[layer], PitchY[layer]) / 2.0; // Best guess +} + +/* + *------------------------------------------------------------ + * And another such routine, for route spacing (minimum width) + *------------------------------------------------------------ + */ + +double +LefGetRouteSpacing(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.spacing->spacing; + } + } + return MIN(PitchX[layer], PitchY[layer]) / 2.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 MIN(PitchX[layer], PitchY[layer]) / 2.0; +} + +/* + *------------------------------------------------------------ + * Get the route pitch for a given layer + *------------------------------------------------------------ + */ + +double +LefGetRoutePitch(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.pitch; + } + } + return MIN(PitchX[layer], PitchY[layer]); +} + +/* + *------------------------------------------------------------ + * 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; +} + +/* + *------------------------------------------------------------ + * 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("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) + if (lreturn) *lreturn = lefl->info.via.obsType; + } + else + { + if (lefl->lefClass != CLASS_IGNORE) + curlayer = lefl->type; + } + } + if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE))) + { + LefError("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("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("Bad port geometry: RECT requires 4 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("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("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("Bad X value in polygon.\n"); + LefEndStatement(f); + break; + } + + token = LefNextToken(f, TRUE); + if (token == NULL || *token == ';') + { + LefError("Missing Y value in polygon point!\n"); + break; + } + if (sscanf(token, "%lg", &py) != 1) + { + LefError("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_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", + "END", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, geometry_keys); + if (keyword < 0) + { + LefError("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_GEOMETRY_END: + if (!LefParseEndStatement(f, NULL)) + { + LefError("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, oscale) + GATE lefMacro; + FILE *f; + char *pinName; + int pinNum, pinDir, pinUse; + float oscale; +{ + DSEG rectList, rlist; + + rectList = LefReadGeometry(lefMacro, f, oscale); + + if (pinName != NULL) lefMacro->node[pinNum] = strdup(pinName); + + if (pinNum >= 0) { + lefMacro->taps[pinNum] = rectList; + if (lefMacro->nodes <= pinNum) + lefMacro->nodes = (pinNum + 1); + } + else { + while (rectList) { + rlist = rectList->next; + free(rectList); + rectList = rlist; + } + } +} + +/* + *------------------------------------------------------------ + * LefReadPin -- + * + * Read a PIN statement from a LEF file. + * + * Results: + * None. + * + * 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_PIN_END}; + +void +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; + + static char *pin_keys[] = { + "DIRECTION", + "USE", + "PORT", + "CAPACITANCE", + "END", + NULL + }; + + static char *pin_classes[] = { + "DEFAULT", + "INPUT", + "OUTPUT TRISTATE", + "OUTPUT", + "INOUT", + "FEEDTHRU", + NULL + }; + + static int lef_class_to_bitmask[] = { + PORT_CLASS_DEFAULT, + PORT_CLASS_INPUT, + PORT_CLASS_TRISTATE, + PORT_CLASS_OUTPUT, + PORT_CLASS_BIDIRECTIONAL, + PORT_CLASS_FEEDTHROUGH + }; + + static char *pin_uses[] = { + "DEFAULT", + "SIGNAL", + "ANALOG", + "POWER", + "GROUND", + "CLOCK", + 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("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("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("Improper USE statement\n"); + else + pinUse = lef_use_to_bitmask[subkey]; + LefEndStatement(f); + break; + case LEF_PORT: + LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, oscale); + break; + case LEF_CAPACITANCE: + LefEndStatement(f); /* Ignore. . . */ + break; + case LEF_PIN_END: + if (!LefParseEndStatement(f, pinname)) + { + LefError("Pin END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_PIN_END) break; + } +} + +/* + *------------------------------------------------------------ + * 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, tsave[128]; + int keyword, pinNum; + 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 + }; + + /* 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("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->width = 0.0; + lefMacro->height = 0.0; + lefMacro->placedX = 0.0; + lefMacro->placedY = 0.0; + lefMacro->obs = (DSEG)NULL; + lefMacro->next = GateInfo; + lefMacro->nodes = 0; + GateInfo = lefMacro; + + /* Initial values */ + pinNum = 0; + has_size = FALSE; + lefBBox.x1 = 0.0; + lefBBox.y1 = 0.0; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, macro_keys); + if (keyword < 0) + { + LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_CLASS: + token = LefNextToken(f, TRUE); + if (*token != '\n') + // DBPropPut(lefMacro, "LEFclass", token); + ; + 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("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("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 + LefReadPin(lefMacro, f, tsave, pinNum++, oscale); + 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: + if (!LefParseEndStatement(f, mname)) + { + LefError("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("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; + } + else + { + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *currect; + viarect->next = lefl->info.via.lr; + lefl->info.via.lr = viarect; + } +} + +/* + *------------------------------------------------------------ + * + * LefReadLayerSection -- + * + * Read in a LAYER, VIA, or VIARULE section from a LEF file. + * + * Results: + * None. + * + * Side Effects: + * Adds to the LEF layer info hash table. + * + *------------------------------------------------------------ + */ + +enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH, + LEF_LAYER_SPACING, LEF_LAYER_SPACINGTABLE, + LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET, + LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT, + LEF_VIARULE_VIA, LEF_LAYER_END}; + +enum lef_spacing_keys {LEF_SPACING_RANGE=0, 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, entries, i; + struct seg_ viaArea; + int curlayer = -1; + double dvalue, oscale; + lefSpacingRule *newrule, *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", + "SPACING", + "SPACINGTABLE", + "PITCH", + "DIRECTION", + "OFFSET", + "DEFAULT", + "LAYER", + "RECT", + "VIA", + "END", + NULL + }; + + static char *spacing_keys[] = { + "RANGE", + ";", + NULL + }; + + /* Database is assumed to be in microns. */ + /* If not, we need to parse the UNITS record, which is */ + /* currently ignored. */ + + oscale = 1; + viaArea.x1 = viaArea.x2 = 0; + viaArea.y1 = viaArea.y2 = 0; + viaArea.layer = -1; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, layer_keys); + if (keyword < 0) + { + LefError("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("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.pitch = 0.0; + lefl->info.route.offset = 0.0; + lefl->info.route.hdirection = (u_char)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 really weirdly written. */ + + if (lefl->type < 0) { + lefl->type = LefGetMaxLayer(); + } + } + else if (typekey == 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; + } + } + else if (lefl->lefClass != typekey) { + LefError("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_WIDTH: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + lefl->info.route.width = dvalue / (double)oscale; + LefEndStatement(f); + break; + case LEF_LAYER_SPACING: + 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) { + newrule->spacing = dvalue / (double)oscale; + newrule->width = 0.0; + newrule->next = lefl->info.route.spacing; + lefl->info.route.spacing = newrule; + } + else { + // 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); + } + 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.pitch = dvalue / (double)oscale; + LefEndStatement(f); + break; + case LEF_LAYER_DIRECTION: + token = LefNextToken(f, TRUE); + LefLower(token); + lefl->info.route.hdirection = (token[0] == 'h') ? TRUE : FALSE; + LefEndStatement(f); + break; + case LEF_LAYER_OFFSET: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + lefl->info.route.offset = dvalue / (double)oscale; + LefEndStatement(f); + break; + case LEF_VIA_DEFAULT: + /* 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: + LefAddViaGeometry(f, lefl, curlayer, oscale); + LefEndStatement(f); + break; + case LEF_VIARULE_VIA: + LefEndStatement(f); + break; + case LEF_LAYER_END: + if (!LefParseEndStatement(f, lname)) + { + LefError("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. + * + *------------------------------------------------------------ + */ + +enum lef_sections {LEF_VERSION = 0, 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}; + +void +LefRead(inName) + char *inName; +{ + FILE *f; + char filename[256]; + char *token; + char tsave[128]; + int keyword, layer; + float oscale; + double xydiff; + LefList lefl; + DSEG grect; + GATE gateginfo; + + static char *sections[] = { + "VERSION", + "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; + } + + if (Verbose > 0) { + Fprintf(stdout, "Reading LEF data from file %s.\n", filename); + Flush(stdout); + } + + oscale = 1; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, sections); + if (keyword < 0) + { + LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_VERSION: + LefEndStatement(f); + break; + case LEF_NAMESCASESENSITIVE: + 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 (lefl == NULL) + { + 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->lefName = strdup(token); + + lefl->next = LefInfo; + LefInfo = lefl; + + LefReadLayerSection(f, tsave, keyword, lefl); + } + else if (keyword == LEF_SECTION_VIARULE) + /* If we've already seen this via, don't reprocess. */ + /* This deals with VIA followed by VIARULE. We */ + /* really ought to have special processing for the */ + /* VIARULE section. . . */ + LefSkipSection(f, tsave); + else + { + LefError("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 = (LefList)malloc(sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_IGNORE; /* For starters */ + lefl->lefName = strdup(token); + lefl->next = LefInfo; + LefInfo = lefl; + } + else + { + if (lefl && lefl->type < 0) + { + LefError("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 (ignored)\n", token); + sprintf(tsave, "%.127s", token); + LefSkipSection(f, tsave); + 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("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(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->gatename = (char *)malloc(4); + strcpy(gateginfo->gatename, "pin"); + gateginfo->node[0] = strdup("pin"); + gateginfo->width = 0.0; + gateginfo->height = 0.0; + gateginfo->placedX = 0.0; + gateginfo->placedY = 0.0; + gateginfo->nodes = 1; + + grect = (DSEG)malloc(sizeof(struct dseg_)); + grect->x1 = grect->x2 = 0.0; + grect->y1 = grect->y2 = 0.0; + grect->next = (DSEG)NULL; + gateginfo->taps[0] = grect; + gateginfo->obs = (DSEG)NULL; + gateginfo->next = GateInfo; + GateInfo = gateginfo; + } + PinMacro = gateginfo; + + /* Work through all of the defined layers, and copy the names into */ + /* the strings used for route output, overriding any information */ + /* that may have been in the route.cfg file. */ + + /* Note that this runs through all the defined vias, from last to */ + /* first defined. Check the X vs. Y dimension of the base layer. */ + /* If X is longer, save as ViaX. If Y is longer, save as ViaY. */ + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass == CLASS_ROUTE) { + strcpy(CIFLayer[lefl->type], lefl->lefName); + } + else if (lefl->lefClass == CLASS_VIA) { + if (lefl->info.via.lr) { + layer = MAX_LAYERS; + if (lefl->info.via.area.layer >= 0) { + layer = lefl->info.via.area.layer; + xydiff = (lefl->info.via.area.x2 - lefl->info.via.area.x1) - + (lefl->info.via.area.y2 - lefl->info.via.area.y1); + } + + for (grect = lefl->info.via.lr; grect; grect = grect->next) { + if (grect->layer >= 0 && grect->layer < layer) { + layer = grect->layer; + xydiff = (grect->x2 - grect->x1) - (grect->y2 - grect->y1); + } + } + if (layer < MAX_LAYERS) { + if (xydiff > -EPS) { + free(ViaX[layer]); + ViaX[layer] = strdup(lefl->lefName); + } + else { + if (ViaY[layer] != NULL) free(ViaY[layer]); + ViaY[layer] = strdup(lefl->lefName); + } + } + } + } + } +} |