Files
magic/lef/lefRead.c
T
Tim Edwards 86b4ac3e4c Modified the "lef read -annotate" method so that pins are checked
for compatible layout over the area of the pin rectangle in the
entire hierarchy of the cell, not just in the top level.  This
corrects issues where pins are placed in the top level cell with
no metal underneath.  It is written in such a way that it will
work regardless of whether paint is split across the hierarchy,
or the label spans different types (such as crossing a contact).
2022-12-12 14:49:42 -05:00

3113 lines
77 KiB
C

/*
* lefRead.c --
*
* This module incorporates the LEF/DEF format for standard-cell place and
* 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.
*
* Note that techfile compatibility requires that each layer name appearing
* in the LEF file should be present as an alias for the appropriate magic
* tile type in the technology file. Layer names are not case sensitive.
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/lef/lefRead.c,v 1.2 2008/12/17 18:40:04 tim Exp $";
#endif /* not lint */
#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> /* for roundf() function, if std=c99 */
#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/undo.h"
#include "utils/utils.h" /* For StrDup() */
#include "database/database.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "dbwind/dbwind.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "cif/cif.h"
#include "cif/CIFint.h" /* Access to CIFCurStyle. . . */
#include "cif/CIFread.h" /* Access to cifCurReadStyle. . . */
#include "lef/lefInt.h"
/* C99 compat */
#include "utils/signals.h"
#include "utils/signals.h"
#include "drc/drc.h"
#include "lef/lef.h"
/* ---------------------------------------------------------------------*/
/* Current line number for reading */
int lefCurrentLine;
/* Cell reading hash tables */
HashTable LefCellTable;
HashTable lefDefInitHash;
/*
*------------------------------------------------------------
*
* LefEstimate --
*
* Estimate the time to completion based on the time,
* the total number of items to process, and the number
* of items processed so far. Attempts to report about
* every 5 seconds or so.
*
* Results:
* None.
*
* Side Effects:
* May print to output.
*
*------------------------------------------------------------
*/
#define PRINT_INTERVAL 5 /* print status at 4 second intervals */
void
LefEstimate(processed, total, item_name)
int processed;
int total;
char *item_name;
{
static int check_interval, partition;
static struct timeval tv_start;
static float last_time;
struct timeval tv;
struct timezone tz;
float cur_time, time_left;
if (!total) return;
if (processed == 0) /* Initialization */
{
gettimeofday(&tv_start, &tz);
GrDisplayStatus = DISPLAY_IN_PROGRESS;
SigSetTimer(PRINT_INTERVAL);
}
else if (processed == total - 1)
{
GrDisplayStatus = DISPLAY_IDLE;
SigRemoveTimer();
}
else if (GrDisplayStatus == DISPLAY_BREAK_PENDING)
{
gettimeofday(&tv, &tz);
cur_time = (float)(tv.tv_sec - tv_start.tv_sec)
+ ((float)(tv.tv_usec - tv_start.tv_usec) / 1.0e6);
time_left = (((float)total / (float)processed) - 1) * cur_time;
/* not likely to happen, but we don't want a divide-by-0 error */
if (cur_time == 0.0) cur_time = 1.0e-6;
TxPrintf(" Processed %d of %d %s (%2.1f%%).", processed, total,
item_name, (float)(100 * processed) / (float)total);
TxPrintf(" Est. time remaining: %2.1fs\n", time_left);
TxFlushOut();
#ifdef MAGIC_WRAPPER
/* We need to let Tk paint the console display */
while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0);
#endif
GrDisplayStatus = DISPLAY_IN_PROGRESS;
SigSetTimer(PRINT_INTERVAL);
}
}
/* This is the original version, which doesn't use the system itimer */
/* and which is not very good at maintaining constant intervals. */
#if 0
void
LefEstimate(processed, total, item_name)
int processed;
int total;
char *item_name;
{
static int check_interval, partition;
static struct timeval tv_start;
static float last_time;
struct timeval tv;
struct timezone tz;
float cur_time, time_left;
if (!total) return;
if (processed == 0) /* Initialization */
{
GrDisplayStatus = DISPLAY_IN_PROGRESS;
check_interval = 100;
gettimeofday(&tv_start, &tz);
last_time = 0.0;
}
if (processed > check_interval)
{
gettimeofday(&tv, &tz);
cur_time = (float)(tv.tv_sec - tv_start.tv_sec)
+ ((float)(tv.tv_usec - tv_start.tv_usec) / 1.0e6);
time_left = (((float)total / (float)processed) - 1) * cur_time;
/* not likely to happen, but we don't want a divide-by-0 error */
if (cur_time == 0.0) cur_time = 1.0e-6;
partition = (int)((float)processed * (float)PRINT_INTERVAL / cur_time);
/* partition is never less than 1 nor greater than 5% of the total */
if (partition == 0) partition = 1;
if (partition > (total / 20)) partition = (total / 20);
check_interval += partition;
/* Don't print anything in intervals faster than 1 second */
if ((cur_time - last_time) < 1.0) return;
last_time = cur_time;
TxPrintf(" Processed %d of %d %s (%2.1f%%).", processed, total,
item_name, (float)(100 * processed) / (float)total);
TxPrintf(" Est. time remaining: %2.1fs\n", time_left);
TxFlushOut();
}
}
#endif /* 0 */
/*
*------------------------------------------------------------
*
* 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(f, ignore_eol)
FILE *f;
bool 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++;
}
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;
}
/*
*------------------------------------------------------------
*
* LefDefError --
*
* Print an error message (via TxError) giving the line
* number of the input file on which the error occurred.
*
* "type" is one of the following (see lef.h):
* LEF_ERROR, LEF_WARNING, LEF_INFO,
* DEF_ERROR, DEF_WARNING, DEF_INFO
*
* Results:
* None.
*
* Side Effects:
* Prints to the output (stderr).
*
*------------------------------------------------------------
*/
void
LefError(int type, char *fmt, ...)
{
static int errors = 0, warnings = 0, messages = 0;
va_list args;
char *lefdeftypes[] = {"LEF", "DEF", "techfile lef section"};
int mode, level;
char *lefdeftype;
switch (type) {
case LEF_INFO:
mode = 0;
level = 0;
break;
case LEF_WARNING:
mode = 0;
level = 1;
break;
case LEF_ERROR:
mode = 0;
level = 2;
break;
case LEF_SUMMARY:
mode = 0;
level = -1;
break;
case DEF_INFO:
mode = 1;
level = 0;
break;
case DEF_WARNING:
mode = 1;
level = 1;
break;
case DEF_ERROR:
mode = 1;
level = 2;
break;
case DEF_SUMMARY:
mode = 1;
level = -1;
break;
}
lefdeftype = lefdeftypes[mode];
if ((fmt == NULL) || (level == -1))
{
/* Special case: report any errors and reset */
if (errors)
TxPrintf("%s Read: encountered %d error%s total.\n",
lefdeftype, errors, (errors == 1) ? "" : "s");
if (warnings)
TxPrintf("%s Read: encountered %d warning%s total.\n",
lefdeftype, warnings, (warnings == 1) ? "" : "s");
errors = 0;
warnings = 0;
messages = 0;
return;
}
switch (level) {
case 2:
if (errors < LEF_MAX_ERRORS)
{
if (lefCurrentLine >= 0)
TxError("%s read, Line %d (Error): ", lefdeftype, lefCurrentLine);
else
TxError("%s read (Error): ", lefdeftype);
va_start(args, fmt);
Vfprintf(stderr, fmt, args);
va_end(args);
TxFlushErr();
}
else if (errors == LEF_MAX_ERRORS)
TxError("%s Read: Further errors will not be reported.\n",
lefdeftype);
errors++;
break;
case 1:
if (warnings < LEF_MAX_ERRORS)
{
if (lefCurrentLine >= 0)
TxError("%s read, Line %d (Warning): ", lefdeftype, lefCurrentLine);
else
TxError("%s read (Warning): ", lefdeftype);
va_start(args, fmt);
Vfprintf(stderr, fmt, args);
va_end(args);
TxFlushErr();
}
else if (warnings == LEF_MAX_ERRORS)
TxError("%s read: Further warnings will not be reported.\n",
lefdeftype);
warnings++;
break;
case 0:
if (messages < LEF_MAX_ERRORS)
{
if (lefCurrentLine >= 0)
TxPrintf("%s read, Line %d (Message): ", lefdeftype, lefCurrentLine);
else
TxPrintf("%s read (Message): ", lefdeftype);
va_start(args, fmt);
Vfprintf(stdout, fmt, args);
va_end(args);
TxFlushOut();
}
else if (messages == LEF_MAX_ERRORS)
TxPrintf("%s read: Further messages will not be reported.\n",
lefdeftype);
messages++;
break;
}
}
/*
*------------------------------------------------------------
*
* 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:
* 1 if the line matches the expected end statement,
* 0 if not. Return -1 if the last token read was
* "END", indicating to the caller that either an
* error has occurred or, in the case of a section
* skip, the routine needs to be called again.
*
* Side effects:
* None.
*
*------------------------------------------------------------
*/
int
LefParseEndStatement(f, match)
FILE *f;
char *match;
{
char *token;
int keyword, words;
char *match_name[2];
static char *end_section[] = {
"END",
"ENDEXT",
NULL
};
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 0;
}
/* END or ENDEXT */
if ((*token == '\n') && (match == NULL)) return 1;
/* END <section_name> */
else {
keyword = LookupFull(token, match_name);
if (keyword == 0)
return 1;
else
{
/* Check for END followed by END */
keyword = LookupFull(token, end_section);
if (keyword == 0)
return -1;
else
return 0;
}
}
}
/*
*------------------------------------------------------------
*
* 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(f, section)
FILE *f;
char *section;
{
char *token;
int keyword, result;
static char *end_section[] = {
"END",
"ENDEXT",
NULL
};
while ((token = LefNextToken(f, TRUE)) != NULL)
{
if ((keyword = Lookup(token, end_section)) == 0)
{
result = -1;
while (result == -1)
{
result = LefParseEndStatement(f, section);
if (result == 1) return;
}
}
else if (keyword == 1)
{
if (!strcmp(section, "BEGINEXT"))
return;
}
}
LefError(LEF_ERROR, "Section %s has no END record!\n", section);
return;
}
/*
*------------------------------------------------------------
*
* lefFindCell --
*
* Search for an existing cell of the given name. If
* it exists, return a pointer to the cell def. If not,
* create a new cell def with the given name and return
* a pointer to it.
*
*------------------------------------------------------------
*/
CellDef *
lefFindCell(name)
char *name; /* Name of the cell to search for */
{
HashEntry *h;
CellDef *def;
h = HashFind(&LefCellTable, name);
if (HashGetValue(h) == 0)
{
def = DBCellLookDef(name);
if (def == NULL)
{
def = DBCellNewDef(name);
DBReComputeBbox(def);
}
HashSetValue(h, def);
}
return (CellDef *) HashGetValue(h);
}
/*
*------------------------------------------------------------
*
* LefLower --
*
* Convert a token in a LEF or DEF file to all-lowercase.
*
*------------------------------------------------------------
*/
char *
LefLower(token)
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.
*
*------------------------------------------------------------
*/
lefLayer *
LefRedefined(lefl, redefname)
lefLayer *lefl;
char *redefname;
{
lefLayer *slef, *newlefl;
char *altName;
LinkedRect *viaLR;
HashSearch hs;
HashEntry *he;
int records;
/* 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;
HashStartSearch(&hs);
while (he = HashNext(&LefInfo, &hs))
{
slef = (lefLayer *)HashGetValue(he);
if (slef == lefl)
records++;
if (altName == NULL)
if (strcmp((char *)he->h_key.h_name, redefname))
altName = (char *)he->h_key.h_name;
}
if (records == 1)
{
/* Only one name associated with the record, so */
/* just clear all the allocated information. */
for (viaLR = lefl->info.via.lr; viaLR != NULL; viaLR = viaLR->r_next)
freeMagic(viaLR);
newlefl = lefl;
}
else
{
he = HashFind(&LefInfo, redefname);
newlefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
newlefl->refCnt = 1;
newlefl->canonName = (char *)he->h_key.h_name;
HashSetValue(he, newlefl);
/* If the canonical name of the original entry */
/* is "redefname", then change it. */
if (!strcmp(lefl->canonName, redefname))
if (altName != NULL)
lefl->canonName = altName;
}
newlefl->type = -1;
newlefl->obsType = -1;
newlefl->info.via.area = GeoNullRect;
newlefl->info.via.cell = (CellDef *)NULL;
newlefl->info.via.lr = (LinkedRect *)NULL;
return newlefl;
}
/*
*------------------------------------------------------------
* LefReadLayers --
*
* Read a LEF "LAYER" record from the file, and
* match the LEF layer to a magic tile type.
* 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 a magic TileType or -1 if no matching
* tile type is found.
*
* Side Effects:
* Reads input from file f;
*
*------------------------------------------------------------
*/
TileType
LefReadLayers(f, obstruct, lreturn, rreturn)
FILE *f;
bool obstruct;
TileType *lreturn;
Rect **rreturn;
{
char *token;
TileType curlayer = -1;
lefLayer *lefl = NULL;
token = LefNextToken(f, TRUE);
if (*token == ';')
{
LefError(LEF_ERROR, "Bad Layer statement\n");
return -1;
}
else
{
HashEntry *he = HashLookOnly(&LefInfo, token);
if (he)
{
lefl = (lefLayer *)HashGetValue(he);
if (lefl && 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)
{
if (lefl->lefClass != CLASS_IGNORE)
curlayer = lefl->type;
if (lefl->lefClass == CLASS_VIA)
if (rreturn) *rreturn = &(lefl->info.via.area);
}
if (rreturn)
{
if (lefl->lefClass == CLASS_VIA)
*rreturn = &(lefl->info.via.area);
else
*rreturn = &GeoNullRect;
}
}
else
{
/* fallback: compare name to magic types */
curlayer = DBTechNameType(token);
if (curlayer < 0)
{
(void) LefLower(token);
curlayer = DBTechNameType(token);
}
}
if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE)))
{
LefError(LEF_ERROR, "Don't know how to parse layer \"%s\"\n", token);
LefError(LEF_ERROR, "Try adding this name to the LEF techfile section\n");
}
}
return curlayer;
}
/*
*------------------------------------------------------------
* LefReadLayer --
*
* Read a LEF "LAYER" record from the file, and
* match the LEF layer to a magic tile type.
* If "obstruct" is TRUE, returns the layer mapping
* for obstruction geometry as defined in the
* technology file (if it exists).
*
* Results:
* Returns a magic TileType or -1 if no matching
* tile type is found.
*
* Side Effects:
* Reads input from file f;
*
*------------------------------------------------------------
*/
TileType
LefReadLayer(f, obstruct)
FILE *f;
bool obstruct;
{
return LefReadLayers(f, obstruct, (TileType *)NULL, (Rect **)NULL);
}
/*
*------------------------------------------------------------
* LefReadLefPoint --
*
* Read a LEF point record from the file, and
* return the two coordinates in LEF units (floating-point).
*
* Results:
* Return 0 on success, 1 on error.
*
* Side Effects:
* Reads input from file f;
* Returns values for the two point coordinates, as type
* float, in "xp" and "yp".
*
* Note:
* Most LEF files do not put parentheses around points, e.g.,
* in the ORIGIN statement, although the LEF specification
* definition of a point insists that it must. Therefore
* we attempt to parse points correctly either way.
*
*------------------------------------------------------------
*/
int
LefReadLefPoint(f, xp, yp)
FILE *f;
float *xp, *yp;
{
char *token;
bool needMatch = FALSE;
token = LefNextToken(f, TRUE);
if (*token == '(')
{
token = LefNextToken(f, TRUE);
needMatch = TRUE;
}
if (!token || sscanf(token, "%f", xp) != 1) return 1;
token = LefNextToken(f, TRUE);
if (!token || sscanf(token, "%f", yp) != 1) return 1;
if (needMatch)
{
token = LefNextToken(f, TRUE);
if (*token != ')') return 1;
}
return 0;
}
/*
*------------------------------------------------------------
* LefReadRect --
*
* Read a LEF "RECT" record from the file, and
* return a Rect in magic coordinates.
*
* Results:
* Returns a pointer to a Rect containing the magic
* 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.
*
*------------------------------------------------------------
*/
Rect *
LefReadRect(f, curlayer, oscale)
FILE *f;
TileType curlayer;
float oscale;
{
char *token;
float llx, lly, urx, ury;
static Rect paintrect;
Rect lefrect;
bool 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)
{
LefError(LEF_ERROR, "No layer defined for RECT.\n");
paintrect.r_xbot = paintrect.r_ybot = 0;
paintrect.r_xtop = paintrect.r_ytop = 0;
}
else
{
/* Scale coordinates (microns to magic internal units) */
/* Need to scale grid if necessary! */
lefrect.r_xbot = (int)roundf(llx / oscale);
lefrect.r_ybot = (int)roundf(lly / oscale);
lefrect.r_xtop = (int)roundf(urx / oscale);
lefrect.r_ytop = (int)roundf(ury / oscale);
/* Insist on non-inverted rectangles */
GeoCanonicalRect(&lefrect, &paintrect);
/* Diagnostic */
/*
TxPrintf(" Painting %s at (%d, %d), (%d, %d) in cell %s.\n",
DBTypeLongNameTbl[curlayer],
paintrect.r_xbot, paintrect.r_ybot,
paintrect.r_xtop, paintrect.r_ytop,
lefMacro->cd_name);
*/
}
return (&paintrect);
parse_error:
LefError(LEF_ERROR, "Bad port geometry: RECT requires 4 values.\n");
return (Rect *)NULL;
}
/*
*------------------------------------------------------------
* LefReadPolygon --
*
* Read a LEF "POLYGON" record from the file, and
* return a linked point structure.
*
* Results:
* Returns a pointer to a Rect containing the magic
* 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.
*
*------------------------------------------------------------
*/
Point *
LefReadPolygon(f, curlayer, oscale, ppoints)
FILE *f;
TileType curlayer;
float oscale;
int *ppoints;
{
LinkedRect *lr = NULL, *newRect;
Point *plist = NULL;
char *token;
float px, py;
int lpoints = 0;
while (1)
{
token = LefNextToken(f, TRUE);
if (token == NULL || *token == ';') break;
if (sscanf(token, "%f", &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, "%f", &py) != 1)
{
LefError(LEF_ERROR, "Bad Y value in polygon.\n");
LefEndStatement(f);
break;
}
/* Use the rect structure for convenience; we only */
/* use the r_ll point of the rect to store each point */
/* as we read it in. */
newRect = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
newRect->r_r.r_xbot = (int)roundf(px / oscale);
newRect->r_r.r_ybot = (int)roundf(py / oscale);
newRect->r_next = lr;
lr = newRect;
lpoints++;
}
*ppoints = lpoints;
if (lpoints == 0) return NULL;
/* Convert LinkedRect structure into a simple point list */
plist = (Point *)mallocMagic(lpoints * sizeof(Point));
lpoints = 0;
while (lr != NULL)
{
plist[*ppoints - lpoints - 1].p_x = lr->r_r.r_xbot;
plist[*ppoints - lpoints - 1].p_y = lr->r_r.r_ybot;
freeMagic(lr);
lpoints++;
lr = lr->r_next;
}
return plist;
}
/*
*------------------------------------------------------------
* LefPaintPolygon --
*
* Paint a polygon into the CellDef indicated by lefMacro.
*
*------------------------------------------------------------
*/
LinkedRect *
LefPaintPolygon(lefMacro, pointList, points, curlayer, keep)
CellDef *lefMacro;
Point *pointList;
int points;
TileType curlayer;
bool keep;
{
int pNum;
PaintUndoInfo ui;
LinkedRect *rlist = NULL, *rptr;
ui.pu_def = lefMacro;
for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
{
if (DBPaintOnPlane(curlayer, pNum))
{
ui.pu_pNum = pNum;
rlist = PaintPolygon(pointList, points, lefMacro->cd_planes[pNum],
DBStdPaintTbl(curlayer, pNum), &ui, keep);
/* Annotate each rectangle in the list with the type painted */
if (keep)
for (rptr = rlist; rptr; rptr = rptr->r_next)
rptr->r_type = curlayer;
}
}
return rlist;
}
/* Structure used by lefConnectFunc(), below */
typedef struct _plane_type {
Plane *pt_plane;
TileType pt_type;
} PlaneType;
/*
*------------------------------------------------------------
* lefConnectFunc --
*
* Callback function used by LefReadGeometry when doing LEF
* annotation to check if a port is compatible with the
* existing layout. Where a compatible type is found, paint
* the type that is passed in the client data argument into
* the plane that is also passed in the client data argument.
* This builds up an area of the port type in the cell
* hierarchy that can be checked against the port rectangle.
*
* Return 0 always to keep the search going.
*------------------------------------------------------------
*/
int
lefConnectFunc(tile, cxp)
Tile *tile;
TreeContext *cxp;
{
SearchContext *scx = cxp->tc_scx;
PlaneType *pt = (PlaneType *)cxp->tc_filter->tf_arg;
Rect rect, trect;
TiToRect(tile, &rect);
GeoTransRect(&scx->scx_trans, &rect, &trect);
/* Where a compatible type is found, paint the type pass as
* argument into the plane passed as argument.
*/
DBPaintPlane(pt->pt_plane, &trect,
DBStdPaintTbl(pt->pt_type, cxp->tc_plane),
(PaintUndoInfo *)NULL);
return 0;
}
/*
*------------------------------------------------------------
* lefUnconnectFunc --
*
* Simple search that returns 1 if any tile is found in the
* plane constructed by lefConnectFunc() above in the area of
* a LEF pin that does not have the same type as the pin.
*
*------------------------------------------------------------
*/
int
lefUnconnectFunc(tile, clientdata)
Tile *tile;
ClientData clientdata; /* (unused) */
{
return 1;
}
/*
*------------------------------------------------------------
* 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. However, if "do_list" is FALSE, then
* LefReadGeometry always returns NULL.
*
* Note that do_list is always TRUE for PORT
* records and FALSE for OBS statements, and so in
* addition to determining what the return value is,
* it also determines what layer is returned by
* function LefReadLayer().
*
* If is_imported is TRUE, then this is an annotation,
* and no geometry should ever be added over an area
* that does not have a compatible type.
*
* Side Effects:
* Reads input from file f;
* Paints into the CellDef lefMacro.
*
*------------------------------------------------------------
*/
enum lef_geometry_keys {LEF_LAYER = 0, LEF_WIDTH, LEF_PATH,
LEF_RECT, LEF_POLYGON, LEF_VIA, LEF_PORT_CLASS,
LEF_GEOMETRY_END};
LinkedRect *
LefReadGeometry(lefMacro, f, oscale, do_list, is_imported)
CellDef *lefMacro;
FILE *f;
float oscale;
bool do_list;
bool is_imported;
{
TileType curlayer = -1, otherlayer = -1;
char *token;
int keyword;
LinkedRect *newRect, *rectList;
Point *pointList;
int points;
Rect *paintrect, *contact = NULL;
static char *geometry_keys[] = {
"LAYER",
"WIDTH",
"PATH",
"RECT",
"POLYGON",
"VIA",
"CLASS",
"END",
NULL
};
rectList = NULL;
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = Lookup(token, geometry_keys);
if (keyword < 0)
{
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
token);
LefEndStatement(f);
continue;
}
switch (keyword)
{
case LEF_LAYER:
curlayer = LefReadLayers(f, (do_list) ? FALSE : TRUE, &otherlayer,
&contact);
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)
{
if (is_imported)
{
int pNum = DBPlane(curlayer);
SearchContext scx;
CellUse dummy;
PlaneType pt;
Plane *checkplane = DBNewPlane((ClientData)0);
scx.scx_use = &dummy;
dummy.cu_def = lefMacro;
dummy.cu_id = NULL;
scx.scx_trans = GeoIdentityTransform;
scx.scx_area = *paintrect;
pt.pt_type = curlayer;
pt.pt_plane = checkplane;
/* Check if layout area is compatible with the port. */
/* Check all paint in this area in all subcells. */
DBTreeSrTiles(&scx, &DBConnectTbl[curlayer], 0,
lefConnectFunc, (ClientData)&pt);
if (DBSrPaintArea((Tile *)NULL, checkplane,
paintrect, &DBNotConnectTbl[curlayer],
lefUnconnectFunc, (ClientData)NULL) == 1)
{
LefEndStatement(f);
TiFreePlane(checkplane);
break;
}
TiFreePlane(checkplane);
}
/* Paint the area, if a CellDef is defined */
if (lefMacro)
{
// Cut layers defined as contacts use the contact
// dimensions, not the dimension from the LEF file
if (DBIsContact(curlayer) && contact && !(GEO_RECTNULL(contact)))
{
paintrect->r_xbot = (paintrect->r_xbot + paintrect->r_xtop);
paintrect->r_ybot = (paintrect->r_ybot + paintrect->r_ytop);
paintrect->r_xtop = paintrect->r_xbot + contact->r_xtop;
paintrect->r_ytop = paintrect->r_ybot + contact->r_ytop;
paintrect->r_xbot += contact->r_xbot;
paintrect->r_ybot += contact->r_ybot;
paintrect->r_xbot >>= 1;
paintrect->r_ybot >>= 1;
paintrect->r_xtop >>= 1;
paintrect->r_ytop >>= 1;
}
DBPaint(lefMacro, paintrect, curlayer);
if ((!do_list) && (otherlayer != -1))
DBPaint(lefMacro, paintrect, otherlayer);
}
/* Remember the area and layer */
if (do_list)
{
newRect = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
newRect->r_type = curlayer;
newRect->r_r = *paintrect;
newRect->r_next = rectList;
rectList = newRect;
}
}
LefEndStatement(f);
break;
case LEF_POLYGON:
pointList = LefReadPolygon(f, curlayer, oscale, &points);
if (pointList)
{
if (lefMacro)
{
LinkedRect *rectNew, *rectTest;
rectNew = LefPaintPolygon(lefMacro, pointList, points,
curlayer, TRUE);
// Add the new list of rectangles to the current list
// of rectangles to be made into port labels.
if (rectList != NULL)
{
for (rectTest = rectList; rectTest && rectTest->r_next;
rectTest = rectTest->r_next);
rectTest->r_next = rectNew;
}
else
rectList = rectNew;
if ((!do_list) && (otherlayer != -1))
LefPaintPolygon(lefMacro, pointList, points, otherlayer);
}
freeMagic(pointList);
}
break;
case LEF_VIA:
LefEndStatement(f);
break;
case LEF_PORT_CLASS:
LefEndStatement(f);
break;
case LEF_GEOMETRY_END:
if (LefParseEndStatement(f, NULL) == 0)
{
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;
* Generates a new label entry in the CellDef lefMacro.
* If "lanno" is not NULL, then the label pointed to by
* lanno is modified. If is_imported is TRUE, then only
* add a shape around lanno (which must be non-NULL). If
* none of the geometry matches the position of the label,
* then move the label to the first area in the geometry
* list.
*
*------------------------------------------------------------
*/
void
LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, pinShape, oscale,
is_imported, lanno)
CellDef *lefMacro;
FILE *f;
char *pinName;
int pinNum, pinDir, pinUse, pinShape;
float oscale;
bool is_imported;
Label *lanno;
{
Label *newlab;
LinkedRect *rectList;
rectList = LefReadGeometry(lefMacro, f, oscale, TRUE, is_imported);
while (rectList != NULL)
{
if ((pinNum >= 0) || (lanno != NULL))
{
/* Label this area */
if (lanno != NULL)
{
/* Modify an existing label */
lanno->lab_rect = rectList->r_r;
lanno->lab_type = rectList->r_type;
/* Pin number is not meaninful in LEF files, so keep */
/* any existing pin number. If original label was not */
/* a port, then find a non-conflicting port number for */
/* it. */
if (lanno->lab_flags & PORT_DIR_MASK)
pinNum = lanno->lab_port;
else
{
Label *sl;
int idx;
pinNum = -1;
for (sl = lefMacro->cd_labels; sl != NULL; sl = sl->lab_next)
{
if (sl->lab_flags & PORT_DIR_MASK)
{
idx = sl->lab_port;
if (idx > pinNum) pinNum = idx;
}
}
pinNum++;
}
}
else
/* Create a new label (non-rendered) */
DBPutLabel(lefMacro, &rectList->r_r, -1, pinName, rectList->r_type, 0, 0);
/* Set this label to be a port */
if (lefMacro->cd_labels == NULL)
LefError(LEF_ERROR, "Internal error: No labels in cell!\n");
else
{
newlab = (lanno != NULL) ? lanno : lefMacro->cd_lastLabel;
if (strcmp(newlab->lab_text, pinName))
LefError(LEF_ERROR, "Internal error: Can't find the label!\n");
else
{
/* Make this a port, and make it a sticky label so that */
/* it is guaranteed to be on the layer on which it is defined */
newlab->lab_flags = pinUse | pinDir | pinShape |
PORT_DIR_MASK | LABEL_STICKY;
newlab->lab_port = pinNum;
}
}
/* If lanno is non-NULL then the first rectangle in the LEF */
/* port list is used to modify it. All other LEF port geometry */
/* generates new labels in the CellDef. */
if (lanno != NULL) lanno = NULL;
}
freeMagic((char *)rectList);
rectList = rectList->r_next;
}
}
/*
*------------------------------------------------------------
* LefReadPin --
*
* Read a PIN statement from a LEF file.
*
* Results:
* None.
*
* Side Effects:
* Reads input from file f;
* Paints into the CellDef 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_ANTENNAPARCUT,
LEF_ANTENNAMAX, LEF_ANTENNAMAXSIDE,
LEF_SHAPE, LEF_NETEXPR, LEF_PIN_END};
void
LefReadPin(lefMacro, f, pinname, pinNum, oscale, is_imported)
CellDef *lefMacro;
FILE *f;
char *pinname;
int pinNum;
float oscale;
bool is_imported;
{
char *token;
char *testpin = pinname;
int keyword, subkey;
int pinDir = PORT_CLASS_DEFAULT;
int pinUse = PORT_USE_DEFAULT;
int pinShape = PORT_SHAPE_DEFAULT;
Label *firstlab;
bool firstport = TRUE;
static char *pin_keys[] = {
"DIRECTION",
"USE",
"PORT",
"CAPACITANCE",
"ANTENNADIFFAREA",
"ANTENNAGATEAREA",
"ANTENNAMODEL",
"ANTENNAPARTIALMETALAREA",
"ANTENNAPARTIALMETALSIDEAREA",
"ANTENNAPARTIALCUTAREA",
"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",
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
};
static char *pin_shapes[] = {
"DEFAULT",
"ABUTMENT",
"RING",
"FEEDTHRU",
NULL
};
static int lef_shape_to_bitmask[] = {
PORT_SHAPE_DEFAULT,
PORT_SHAPE_ABUT,
PORT_SHAPE_RING,
PORT_SHAPE_THRU
};
/* If LEF file is being imported to annotate an existing layout, then
* find the first label in the layout that matches the PIN record.
* If there are no matches, then test for various issues with
* delimiter translation and case sensitivity.
*/
if (is_imported)
{
/* Find the first matching label */
for (firstlab = lefMacro->cd_labels; firstlab; firstlab = firstlab->lab_next)
if (!strcmp(firstlab->lab_text, testpin))
break;
/* If no match was found, look for common issues */
/* such as bus delimiter translations or case */
/* sensitivity. */
if (firstlab == NULL)
{
char *delim;
testpin = (char *)mallocMagic(strlen(pinname) + 1);
strcpy(testpin, pinname);
if ((delim = strchr(testpin, '<')) != NULL)
{
*delim = '[';
if ((delim = strchr(testpin, '>')) != NULL)
*delim = ']';
}
else if ((delim = strchr(testpin, '[')) != NULL)
{
char *delim2;
*delim = '<';
if ((delim2 = strchr(testpin, ']')) != NULL)
*delim2 = '>';
}
for (firstlab = lefMacro->cd_labels; firstlab; firstlab = firstlab->lab_next)
if (!strcmp(firstlab->lab_text, testpin))
break;
if (firstlab == NULL) /* No luck, so return to original */
{
freeMagic(testpin);
testpin = pinname;
}
}
if (firstlab == NULL)
{
for (firstlab = lefMacro->cd_labels; firstlab; firstlab = firstlab->lab_next)
if (!strcasecmp(firstlab->lab_text, testpin))
break;
if (firstlab != NULL) /* Case matching succeeded */
{
if (testpin == pinname)
testpin = (char *)mallocMagic(strlen(pinname) + 1);
strcpy(testpin, firstlab->lab_text);
}
}
if (firstlab == NULL) firstlab = lefMacro->cd_labels;
}
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = Lookup(token, pin_keys);
if (keyword < 0)
{
LefError(LEF_INFO, "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_SHAPE:
token = LefNextToken(f, TRUE);
subkey = Lookup(token, pin_shapes);
if (subkey < 0)
LefError(LEF_ERROR, "Improper SHAPE statement\n");
else
pinShape = lef_shape_to_bitmask[subkey];
LefEndStatement(f);
break;
case LEF_PORT:
if (is_imported)
{
bool needRect = TRUE;
bool hasPort = FALSE;
Label *lab;
/* Conflicting interests: One purpose of annotation */
/* is to make ports where none existed. But if the */
/* cell has both port and non-port labels with the */
/* same string, then don't mess with the non-port */
/* label. */
for (lab = firstlab; lab; lab = lab->lab_next)
if (lab->lab_flags & PORT_DIR_MASK)
if (!strcmp(lab->lab_text, testpin))
{
hasPort = TRUE;
break;
}
/* Skip the port geometry but find the pin name and */
/* annotate with the use and direction. Note that */
/* there may be multiple instances of the label. */
/* However, if the label is a point label, then */
/* replace it with the geometry from the LEF file. */
if (hasPort == FALSE) lab = firstlab;
for (; lab; lab = lab->lab_next)
{
if (!strcmp(lab->lab_text, testpin))
{
/* If there is at least one port label with this */
/* name, then ignore all non-port labels with the */
/* same name. */
if ((hasPort) && (!(lab->lab_flags & PORT_DIR_MASK)))
break;
else if (GEO_RECTNULL(&lab->lab_rect))
break;
else
{
if (lab->lab_flags & PORT_DIR_MASK)
pinNum = lab->lab_port;
else
{
Label *sl;
int idx;
pinNum = -1;
for (sl = lefMacro->cd_labels; sl != NULL;
sl = sl->lab_next)
{
if (sl->lab_flags & PORT_DIR_MASK)
{
idx = sl->lab_port;
if (idx > pinNum) pinNum = idx;
}
}
pinNum++;
}
needRect = FALSE;
lab->lab_flags &= ~(PORT_USE_MASK | PORT_DIR_MASK |
PORT_CLASS_MASK | PORT_SHAPE_MASK);
lab->lab_flags |= pinUse | pinDir | pinShape |
PORT_DIR_MASK;
lab->lab_port = pinNum;
}
}
}
firstlab = (lab == NULL) ? NULL : lab->lab_next;
if (needRect)
{
if ((lab == NULL) && (firstport == TRUE))
DBEraseLabelsByContent(lefMacro, NULL, -1, testpin);
LefReadPort(lefMacro, f, testpin, pinNum, pinDir, pinUse,
pinShape, oscale, TRUE, lab);
}
else
LefSkipSection(f, NULL);
firstport = FALSE;
}
else
LefReadPort(lefMacro, f, testpin, pinNum, pinDir, pinUse,
pinShape, oscale, FALSE, NULL);
break;
case LEF_CAPACITANCE:
case LEF_ANTENNADIFF:
case LEF_ANTENNAGATE:
case LEF_ANTENNAMOD:
case LEF_ANTENNAPAR:
case LEF_ANTENNAPARSIDE:
case LEF_ANTENNAPARCUT:
case LEF_ANTENNAMAX:
case LEF_ANTENNAMAXSIDE:
case LEF_NETEXPR:
LefEndStatement(f); /* Ignore. . . */
break;
case LEF_PIN_END:
if (LefParseEndStatement(f, pinname) == 0)
{
LefError(LEF_ERROR, "Pin END statement missing.\n");
keyword = -1;
}
break;
}
if (keyword == LEF_PIN_END) break;
}
if (testpin != pinname) freeMagic(testpin);
}
/*
*------------------------------------------------------------
* LefEndStatement --
*
* Read file input to EOF or a ';' token (end-of-statement)
*
*------------------------------------------------------------
*/
void
LefEndStatement(f)
FILE *f;
{
char *token;
while ((token = LefNextToken(f, TRUE)) != NULL)
if (*token == ';') break;
}
/*
*------------------------------------------------------------
*
* LefReadNonDefaultRule --
*
* Read in a NONDEFAULTRULE section from a LEF file.
*
* Results:
* None.
*
* Side Effects:
* Creates a new nondefaultrule in the associated hash
* table.
*
*------------------------------------------------------------
*/
enum lef_nondefprop_keys {
LEF_NONDEFPROP_HARDSPACING = 0, LEF_NONDEFPROP_LAYER,
LEF_NONDEFPROP_END, LEF_NONDEFPROP_VIA,
LEF_NONDEFPROP_USEVIA, LEF_NONDEFPROP_USEVIARULE,
LEF_NONDEFPROP_MINCUTS, LEF_NONDEFPROP_PROPERTY,
LEF_NONDEFLAYER_WIDTH, LEF_NONDEFLAYER_DIAG,
LEF_NONDEFLAYER_SPACE, LEF_NONDEFLAYER_EXT};
void
LefReadNonDefaultRule(f, rname, oscale)
FILE *f; /* LEF file being read */
char *rname; /* name of the rule */
float oscale; /* scale factor um->magic units */
{
char *token;
char tsave[128];
int keyword;
HashEntry *he;
LefRules *ruleset = NULL;
lefRule *rule = NULL;
lefLayer *lefl;
float fvalue;
char *newname = rname;
int idx;
bool inlayer, done;
static char *nondef_property_keys[] = {
"HARDSPACING",
"LAYER",
"END",
"VIA",
"USEVIA",
"USEVIARULE",
"MINCUTS",
"PROPERTY",
"WIDTH",
"DIAGWIDTH",
"SPACING",
"WIREEXTENSION",
NULL
};
/* Start by creating a new nondefault rule */
he = HashFind(&LefNonDefaultRules, rname);
idx = 0;
if (HashGetValue(he) != NULL)
{
LefError(LEF_WARNING, "Nondefault rule \"%s\" was already defined.\n", rname);
newname = (char *)mallocMagic(strlen(rname) + 5);
}
while (HashGetValue(he))
{
sprintf(newname, "%s_%d", rname, idx);
LefError(LEF_WARNING, "Renaming this rule \"%s\"\n", newname);
he = HashFind(&LefNonDefaultRules, newname);
}
ruleset = (LefRules *)mallocMagic(sizeof(LefRules));
HashSetValue(he, ruleset);
ruleset->name = StrDup((char **)NULL, newname);
ruleset->rule = NULL;
done = FALSE;
inlayer = FALSE;
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = Lookup(token, nondef_property_keys);
if (keyword < 0)
{
LefError(LEF_INFO, "Unknown keyword \"%s\" in NONDEFAULTRULES "
"definition; ignoring.\n", token);
LefEndStatement(f);
continue;
}
switch (keyword)
{
case LEF_NONDEFPROP_END:
token = LefNextToken(f, TRUE);
if (inlayer)
inlayer = FALSE;
else
done = TRUE;
case LEF_NONDEFPROP_HARDSPACING:
lefl = NULL;
/* Ignore this */
break;
case LEF_NONDEFPROP_VIA:
/* Read in the via definition */
token = LefNextToken(f, TRUE);
sprintf(tsave, "%.127s", token);
he = HashFind(&LefInfo, token);
lefl = (lefLayer *)HashGetValue(he);
if (lefl == NULL)
{
lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
lefl->type = -1;
lefl->obsType = -1;
lefl->refCnt = 1;
lefl->lefClass = CLASS_VIA;
lefl->info.via.area = GeoNullRect;
lefl->info.via.cell = (CellDef *)NULL;
lefl->info.via.lr = (LinkedRect *)NULL;
HashSetValue(he, lefl);
LefReadLayerSection(f, tsave, keyword, lefl);
lefl->canonName = (char *)he->h_key.h_name;
}
goto newrule;
case LEF_NONDEFPROP_USEVIA:
case LEF_NONDEFPROP_USEVIARULE:
lefl = NULL;
/* Fall through---vias and viarules are treated like layers */
case LEF_NONDEFPROP_LAYER:
token = LefNextToken(f, TRUE);
he = HashFind(&LefInfo, token);
lefl = (lefLayer *)HashGetValue(he);
newrule:
if (ruleset)
{
/* Chain new layer rule to linked list */
rule = (lefRule *)mallocMagic(sizeof(lefRule));
rule->lefInfo = lefl;
rule->width = 0;
rule->spacing = 0;
rule->extend = 0;
rule->next = ruleset->rule;
ruleset->rule = rule;
}
else
LefError(LEF_INFO, "No non-default rule name for \"%s\" "
"in NONDEFAULTRULE definition!.\n", token);
if (keyword == LEF_NONDEFPROP_LAYER) inlayer = TRUE;
break;
case LEF_NONDEFPROP_MINCUTS:
case LEF_NONDEFPROP_PROPERTY:
lefl = NULL;
/* Ignore the next two tokens */
token = LefNextToken(f, TRUE);
token = LefNextToken(f, TRUE);
break;
case LEF_NONDEFLAYER_WIDTH:
if (!inlayer)
LefError(LEF_INFO, "WIDTH specified without layer.\n");
token = LefNextToken(f, TRUE);
sscanf(token, "%f", &fvalue);
if (rule == NULL)
LefError(LEF_INFO, "No rule for non-default width.\n");
else if (lefl == NULL)
LefError(LEF_INFO, "No layer for non-default width.\n");
else
rule->width = (int)roundf(fvalue / oscale);
LefEndStatement(f);
break;
case LEF_NONDEFLAYER_SPACE:
if (!inlayer)
LefError(DEF_INFO, "SPACING specified without layer.\n");
token = LefNextToken(f, TRUE);
sscanf(token, "%f", &fvalue);
if (rule == NULL)
LefError(LEF_INFO, "No rule for non-default spacing.\n");
else if (lefl == NULL)
LefError(LEF_INFO, "No layer for non-default spacing.\n");
else
rule->spacing = (int)roundf(fvalue / oscale);
LefEndStatement(f);
break;
case LEF_NONDEFLAYER_EXT:
if (!inlayer)
LefError(DEF_INFO, "WIREEXT specified without layer.\n");
token = LefNextToken(f, TRUE);
sscanf(token, "%f", &fvalue);
if (rule == NULL)
LefError(LEF_INFO, "No rule for non-default extension.\n");
else if (lefl == NULL)
LefError(LEF_INFO, "No layer for non-default extension.\n");
else
rule->extend = (int)roundf((2 * fvalue) / oscale);
LefEndStatement(f);
break;
case LEF_NONDEFLAYER_DIAG:
if (!inlayer)
LefError(LEF_INFO,
"Layer value specified without layer.\n");
/* Absorb token and ignore */
token = LefNextToken(f, TRUE);
LefEndStatement(f);
break;
}
if (done) break;
}
if (newname != rname) freeMagic(newname);
}
/*
*------------------------------------------------------------
*
* LefReadMacro --
*
* Read in a MACRO section from a LEF file.
*
* Results:
* None.
*
* Side Effects:
* Creates a new cell definition in the magic 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_PROPERTY, LEF_MACRO_END};
void
LefReadMacro(f, mname, oscale, importForeign, doAnnotate, lefTimestamp)
FILE *f; /* LEF file being read */
char *mname; /* name of the macro */
float oscale; /* scale factor um->magic units */
bool importForeign; /* Whether we should try to read
* in a cell.
*/
bool doAnnotate; /* If true, ignore all macros that are
* not already CellDefs.
*/
int lefTimestamp; /* If not -1, use the value pointed to
* as the CellDef's timestamp.
*/
{
CellDef *lefMacro;
HashEntry *he;
char *token, tsave[128], *propval;
int keyword, pinNum, propsize;
float x, y;
bool has_size, is_imported = FALSE, propfound;
Rect lefBBox;
static char *macro_keys[] = {
"CLASS",
"SIZE",
"ORIGIN",
"SYMMETRY",
"SOURCE",
"SITE",
"PIN",
"OBS",
"TIMING",
"FOREIGN",
"PROPERTY",
"END",
NULL
};
/* Start by creating a new celldef */
he = HashFind(&lefDefInitHash, mname);
if (HashGetValue(he))
{
int suffix;
char newname[256];
for (suffix = 1; HashGetValue(he) != NULL; suffix++)
{
snprintf(newname, 255, "%s_%d", mname, suffix);
he = HashFind(&lefDefInitHash, newname);
}
LefError(LEF_WARNING, "Cell \"%s\" was already defined in this file. "
"Renaming this cell \"%s\"\n", mname, newname);
lefMacro = DBCellLookDef(newname);
if (lefMacro == NULL)
{
if (doAnnotate)
{
/* Ignore any macro that does not correspond to an existing cell */
LefSkipSection(f, "MACRO");
return;
}
lefMacro = lefFindCell(newname);
DBCellClearDef(lefMacro);
DBCellSetAvail(lefMacro);
HashSetValue(he, lefMacro);
is_imported = FALSE;
}
else
is_imported = TRUE;
}
else
{
lefMacro = DBCellLookDef(mname);
if (lefMacro == NULL)
{
lefMacro = lefFindCell(mname);
DBCellClearDef(lefMacro);
DBCellSetAvail(lefMacro);
HashSetValue(he, lefMacro);
is_imported = FALSE;
}
else
is_imported = TRUE;
}
if (lefTimestamp != -1)
{
lefMacro->cd_timestamp = lefTimestamp;
lefMacro->cd_flags |= CDFIXEDSTAMP;
}
/* Initial values */
pinNum = 1;
has_size = FALSE;
lefBBox.r_xbot = 0;
lefBBox.r_ybot = 0;
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = Lookup(token, macro_keys);
if (keyword < 0)
{
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
token);
LefEndStatement(f);
continue;
}
switch (keyword)
{
case LEF_CLASS:
strcpy(tsave, "");
token = LefNextToken(f, TRUE);
while (*token != ';')
{
sprintf(tsave + strlen(tsave), " %s", token);
token = LefNextToken(f, TRUE);
}
DBPropPut(lefMacro, "LEFclass", StrDup((char **)NULL, tsave + 1));
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.r_xtop = (int)roundf(x / oscale) + lefBBox.r_xbot;
lefBBox.r_ytop = (int)roundf(y / oscale) + lefBBox.r_ybot;
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:
if (LefReadLefPoint(f, &x, &y) != 0) goto origin_error;
lefBBox.r_xbot = -(int)roundf(x / oscale);
lefBBox.r_ybot = -(int)roundf(y / oscale);
if (has_size)
{
lefBBox.r_xtop += lefBBox.r_xbot;
lefBBox.r_ytop += lefBBox.r_ybot;
}
LefEndStatement(f);
break;
origin_error:
LefError(LEF_ERROR, "Bad macro ORIGIN; requires 2 values.\n");
LefEndStatement(f);
break;
case LEF_SYMMETRY:
strcpy(tsave, "");
token = LefNextToken(f, TRUE);
while (*token != ';')
{
sprintf(tsave + strlen(tsave), " %s", token);
token = LefNextToken(f, TRUE);
}
DBPropPut(lefMacro, "LEFsymmetry", StrDup((char **)NULL, tsave + 1));
break;
case LEF_SOURCE:
token = LefNextToken(f, TRUE);
/* Ignore "SOURCE" as it is deprecated from LEF 5.6, and */
/* magic will write LEF version 5.7. */
LefEndStatement(f);
break;
case LEF_SITE:
token = LefNextToken(f, TRUE);
if (*token != '\n')
DBPropPut(lefMacro, "LEFsite", StrDup((char **)NULL, token));
LefEndStatement(f);
break;
case LEF_PROPERTY:
/* Append property key:value pairs to the cell property LEFproperties */
propval = (char *)DBPropGet(lefMacro, "LEFproperties", &propfound);
if (propfound)
propsize = strlen(propval);
else
propsize = 0;
token = LefNextToken(f, TRUE);
if (*token != '\n')
{
char *propext;
sprintf(tsave, "%.127s", token);
token = LefNextToken(f, TRUE);
propext = (char *)mallocMagic(propsize + strlen(tsave) +
strlen(token) + 4);
if (propsize > 0)
sprintf(propext, "%s %s %s", propval, tsave, token);
else
sprintf(propext, "%s %s", tsave, token);
DBPropPut(lefMacro, "LEFproperties", StrDup((char **)NULL, propext));
}
LefEndStatement(f);
break;
case LEF_PIN:
token = LefNextToken(f, TRUE);
/* Diagnostic */
/*
TxPrintf(" Macro defines pin %s\n", token);
*/
sprintf(tsave, "%.127s", token);
LefReadPin(lefMacro, f, tsave, pinNum++, oscale, is_imported);
break;
case LEF_OBS:
/* Diagnostic */
/*
TxPrintf(" Macro defines obstruction\n");
*/
if (is_imported)
LefSkipSection(f, NULL);
else
LefReadGeometry(lefMacro, f, oscale, FALSE, is_imported);
break;
case LEF_TIMING:
LefSkipSection(f, macro_keys[LEF_TIMING]);
break;
case LEF_FOREIGN:
if (importForeign)
{
token = LefNextToken(f, TRUE);
sprintf(tsave, "%.127s", token);
/* To do: Read and apply X and Y offsets */
}
LefEndStatement(f);
break;
case LEF_MACRO_END:
if (LefParseEndStatement(f, mname) == 0)
{
LefError(LEF_ERROR, "Macro END statement missing.\n");
keyword = -1;
}
break;
}
if (keyword == LEF_MACRO_END) break;
}
/* Finish up creating the cell */
DBReComputeBbox(lefMacro);
if (is_imported)
{
/* Define the FIXED_BBOX property to match the LEF macro */
if (has_size)
{
lefMacro->cd_flags |= CDFIXEDBBOX;
propval = (char *)mallocMagic(40);
sprintf(propval, "%d %d %d %d",
lefBBox.r_xbot, lefBBox.r_ybot,
lefBBox.r_xtop, lefBBox.r_ytop);
DBPropPut(lefMacro, "FIXED_BBOX", propval);
}
}
else
{
DBAdjustLabelsNew(lefMacro, &TiPlaneRect, 1);
if (has_size)
{
lefMacro->cd_flags |= CDFIXEDBBOX;
propval = (char *)mallocMagic(40);
sprintf(propval, "%d %d %d %d",
lefBBox.r_xbot, lefBBox.r_ybot,
lefBBox.r_xtop, lefBBox.r_ytop);
DBPropPut(lefMacro, "FIXED_BBOX", propval);
}
else
{
LefError(LEF_WARNING, " Macro does not define size: "
"computing from geometry\n");
/* Set the placement bounding box property to the current bounding box */
lefMacro->cd_flags |= CDFIXEDBBOX;
propval = (char *)mallocMagic(40);
sprintf(propval, "%d %d %d %d",
lefMacro->cd_bbox.r_xbot,
lefMacro->cd_bbox.r_ybot,
lefMacro->cd_bbox.r_xtop,
lefMacro->cd_bbox.r_ytop);
DBPropPut(lefMacro, "FIXED_BBOX", propval);
DRCCheckThis(lefMacro, TT_CHECKPAINT, &lefMacro->cd_bbox);
}
}
/* Note: When the LEF view is used to annotate an */
/* existing GDS view, then the view is not considered */
/* "abstract"; otherwise, writing GDS output gets very */
/* complicated and inefficient. */
/* Note: The value here is ignored, setting to "TRUE". */
/* The "extract" command only cares that the key exists. */
/* i.e., setting it to "FALSE" would be ineffective. */
if (!is_imported)
DBPropPut(lefMacro, "LEFview", StrDup((char **)NULL, "TRUE"));
DBWAreaChanged(lefMacro, &lefMacro->cd_bbox, DBW_ALLWINDOWS,
&DBAllButSpaceBits);
}
/*
*------------------------------------------------------------
*
* LefGrowVia ---
*
* For LEF contact types matching magic contact types, size the
* LEF contact cut to cover the minimum rectangle in the other
* layers that satisfies the CIF/GDS contact generation.
*
* NOTE: If a "cifinput" style is defined, then the via
* remains the size declared in LEF or DEF file, and the
* magic view of the via is generated by applying "cifinput"
* rules when painting into the magic database. If no input
* style is defined, then the output style rules are used to
* modify the cut size to match the way the via is defined in
* magic, and the result is painted directly.
*
* If a "cifinput" style exists, then this routine does
* nothing and has no side effects.
*
*------------------------------------------------------------
*/
void LefGrowVia(curlayer, currect, lefl)
TileType curlayer;
Rect *currect;
lefLayer *lefl;
{
/* To be completed: This should be deprecated by moving the entire */
/* LEF and DEF read routines to use the cifinput style. */
if (DBIsContact(curlayer) && CIFCurStyle != NULL)
{
int edgeSize = 0, contSize, halfSize;
/* Get the minimum size of a contact (cut + borders) from cifoutput */
contSize = CIFGetContactSize(curlayer, &edgeSize, NULL, NULL);
/* All internal LEF via geometry values are doubled */
contSize <<= 1;
edgeSize <<= 1;
if (contSize % CIFCurStyle->cs_scaleFactor == 0)
contSize /= CIFCurStyle->cs_scaleFactor;
else
contSize = contSize / CIFCurStyle->cs_scaleFactor + 1;
if (edgeSize % CIFCurStyle->cs_scaleFactor == 0)
edgeSize /= CIFCurStyle->cs_scaleFactor;
else
edgeSize = edgeSize / CIFCurStyle->cs_scaleFactor + 1;
if (edgeSize > 0 && contSize > 0)
{
/* Flag a warning if the cut size is different from what's expected */
if ((currect->r_xtop - currect->r_xbot != edgeSize) ||
(currect->r_ytop - currect->r_ybot != edgeSize))
{
LefError(LEF_WARNING, "Cut size for magic type \"%s\" (%d x %d) does "
"not match LEF/DEF\n",
DBTypeLongNameTbl[lefl->type],
edgeSize, edgeSize);
LefError(LEF_WARNING, "Via cut size (%d x %d). Magic layer "
"cut size will be used!\n",
currect->r_xtop - currect->r_xbot,
currect->r_ytop - currect->r_ybot);
}
halfSize = contSize >> 1;
currect->r_xbot = ((currect->r_xbot + currect->r_xtop) / 2) - halfSize;
currect->r_ybot = ((currect->r_ybot + currect->r_ytop) / 2) - halfSize;
currect->r_xtop = currect->r_xbot + contSize;
currect->r_ytop = currect->r_ybot + contSize;
}
}
}
/*
*------------------------------------------------------------
*
* LefGenViaGeometry --
*
* Create geometry for a VIA section from a DEF file
* using via generation parameters.
*
* Results:
* None.
*
* Side Effects:
* Adds to the lefLayer record for a via definition.
*
*------------------------------------------------------------
*/
void
LefGenViaGeometry(f, lefl, sizex, sizey, spacex, spacey,
encbx, encby, enctx, encty, rows, cols,
tlayer, clayer, blayer, oscale)
FILE *f; /* LEF file being read */
lefLayer *lefl; /* pointer to via info */
int sizex, sizey; /* cut size */
int spacex, spacey; /* cut spacing */
int encbx, encby; /* bottom enclosure of cuts */
int enctx, encty; /* top enclosure of cuts */
int rows, cols; /* number of cut rows and columns */
TileType tlayer; /* Top layer type */
TileType clayer; /* Cut layer type */
TileType blayer; /* Bottom layer type */
float oscale; /* output scaling */
{
Rect rect;
int i, j, x, y, w, h, sw, sh;
LinkedRect *viaLR;
float hscale = oscale / 2;
/* Compute top layer rect */
w = (sizex * cols) + (spacex * (cols - 1)) + 2 * enctx;
h = (sizey * rows) + (spacey * (rows - 1)) + 2 * encty;
rect.r_xtop = (int)roundf(w / oscale);
rect.r_xbot = -rect.r_xtop;
rect.r_ytop = (int)roundf(h / oscale);
rect.r_ybot = -rect.r_ytop;
/* Set via area to the top layer */
lefl->info.via.area = rect;
lefl->type = tlayer;
/* Compute bottom layer rect */
w = (sizex * cols) + (spacex * (cols - 1)) + 2 * encbx;
h = (sizey * rows) + (spacey * (rows - 1)) + 2 * encby;
rect.r_xtop = (int)roundf(w / oscale);
rect.r_xbot = -rect.r_xtop;
rect.r_ytop = (int)roundf(h / oscale);
rect.r_ybot = -rect.r_ytop;
viaLR = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
viaLR->r_next = lefl->info.via.lr;
lefl->info.via.lr = viaLR;
viaLR->r_type = blayer;
viaLR->r_r = rect;
w = (sizex * cols) + (spacex * (cols - 1));
h = (sizey * rows) + (spacey * (rows - 1));
x = -w / 2;
y = -h / 2;
for (i = 0; i < cols; i++)
{
for (j = 0; j < rows; j++)
{
rect.r_xbot = (int)roundf(x / hscale);
rect.r_ybot = (int)roundf(y / hscale);
rect.r_xtop = rect.r_xbot + (int)roundf(sizex / hscale);
rect.r_ytop = rect.r_ybot + (int)roundf(sizey / hscale);
/* Expand via to the size used by magic */
LefGrowVia(clayer, &rect, lefl);
viaLR = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
viaLR->r_next = lefl->info.via.lr;
lefl->info.via.lr = viaLR;
viaLR->r_type = clayer;
viaLR->r_r = rect;
y += sizey + spacey;
}
x += sizex + spacex;
y = -h / 2;
}
}
/*
*------------------------------------------------------------
*
* LefAddViaGeometry --
*
* Read in geometry for a VIA section from a LEF or DEF
* file.
*
* Results:
* None.
*
* Side Effects:
* Adds to the lefLayer record for a via definition.
*
*------------------------------------------------------------
*/
void
LefAddViaGeometry(f, lefl, curlayer, oscale)
FILE *f; /* LEF file being read */
lefLayer *lefl; /* pointer to via info */
TileType curlayer; /* current tile type */
float oscale; /* output scaling */
{
Rect *currect;
LinkedRect *viaLR;
/* Rectangles for vias are read in units of 1/2 lambda */
currect = LefReadRect(f, curlayer, (oscale / 2));
if (currect == NULL) return;
/* Don't create any geometry for unknown layers! */
if (curlayer < 0) return;
/* Expand via to the size used by magic */
LefGrowVia(curlayer, currect, lefl);
if (GEO_SAMERECT(lefl->info.via.area, GeoNullRect))
{
lefl->info.via.area = *currect;
lefl->type = curlayer;
}
else
{
viaLR = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
viaLR->r_next = lefl->info.via.lr;
lefl->info.via.lr = viaLR;
viaLR->r_type = curlayer;
viaLR->r_r = *currect;
/* Make sure that the primary record is a contact type. */
if (DBIsContact(curlayer) && !DBIsContact(lefl->type))
{
viaLR->r_type = lefl->type;
lefl->type = curlayer;
viaLR->r_r = lefl->info.via.area;
lefl->info.via.area = *currect;
}
}
}
/*
*------------------------------------------------------------
*
* 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_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_PROPERTY, LEF_LAYER_ACDENSITY, LEF_LAYER_DCDENSITY,
LEF_LAYER_MINDENSITY, LEF_LAYER_ANTENNADIFF,
LEF_LAYER_ANTENNAAREA, LEF_LAYER_ANTENNASIDE,
LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT, LEF_VIA_FOREIGN,
LEF_VIA_ENCLOSURE, LEF_VIA_PREFERENCLOSURE,
LEF_VIARULE_OVERHANG,
LEF_VIARULE_METALOVERHANG, LEF_VIARULE_VIA,
LEF_VIARULE_GENERATE, LEF_LAYER_END};
void
LefReadLayerSection(f, lname, mode, lefl)
FILE *f; /* LEF file being read */
char *lname; /* name of the layer */
int mode; /* layer, via, or viarule */
lefLayer *lefl; /* pointer to layer info */
{
char *token;
int keyword, typekey;
Rect viaArea;
TileType curlayer = -1;
float fvalue, oscale;
/* 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",
"MAXWIDTH",
"AREA",
"SPACING",
"SPACINGTABLE",
"PITCH",
"DIRECTION",
"OFFSET",
"WIREEXTENSION",
"RESISTANCE",
"CAPACITANCE",
"EDGECAPACITANCE",
"THICKNESS",
"HEIGHT",
"PROPERTY",
"ACCURRENTDENSITY",
"DCCURRENTDENSITY",
"MINIMUMDENSITY",
"ANTENNAAREARATIO",
"ANTENNADIFFAREARATIO",
"ANTENNASIDEAREARATIO",
"DEFAULT",
"LAYER",
"RECT",
"FOREIGN",
"ENCLOSURE",
"PREFERENCLOSURE",
"OVERHANG",
"METALOVERHANG",
"VIA",
"GENERATE",
"END",
NULL
};
static char *spacing_keys[] = {
"RANGE",
";",
NULL
};
oscale = CIFGetOutputScale(1000);
viaArea = GeoNullRect;
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = Lookup(token, layer_keys);
if (keyword < 0)
{
LefError(LEF_INFO, "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 != typekey)
{
/* ROUTE and VIA are taken from techfile lef section */
/* and so having a different TYPE is an error. */
/* Otherwise just ignore the type. */
if (typekey == CLASS_ROUTE || typekey == CLASS_VIA)
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_WIDTH:
token = LefNextToken(f, TRUE);
sscanf(token, "%f", &fvalue);
if (lefl->lefClass == CLASS_ROUTE)
lefl->info.route.width = (int)roundf(fvalue / oscale);
LefEndStatement(f);
break;
case LEF_LAYER_MAXWIDTH:
case LEF_LAYER_AREA:
/* Not handled */
LefEndStatement(f);
break;
case LEF_LAYER_SPACING:
/* To do: Handle RANGE */
token = LefNextToken(f, TRUE);
sscanf(token, "%f", &fvalue);
if (lefl->lefClass == CLASS_ROUTE)
lefl->info.route.spacing = (int)roundf(fvalue / oscale);
LefEndStatement(f);
break;
case LEF_LAYER_PROPERTY:
/* Ignoring property statements */
LefEndStatement(f);
break;
case LEF_LAYER_ACDENSITY:
/* Stupid syntax */
token = LefNextToken(f, TRUE); /* value type */
token = LefNextToken(f, TRUE); /* value, FREQUENCY */
if (!strcmp(token, "FREQUENCY")) {
LefEndStatement(f);
token = LefNextToken(f, TRUE);
if (!strcmp(token, "WIDTH")) /* Optional width */
LefEndStatement(f); /* Additional TABLEENTRIES */
}
LefEndStatement(f);
break;
case LEF_LAYER_DCDENSITY:
/* Stupid syntax */
token = LefNextToken(f, TRUE); /* value type */
token = LefNextToken(f, TRUE); /* value, WIDTH */
if (!strcmp(token, "WIDTH"))
LefEndStatement(f); /* Additional TABLEENTRIES */
LefEndStatement(f);
break;
case LEF_LAYER_SPACINGTABLE:
/* To do: Handle spacing tables */
LefEndStatement(f);
break;
case LEF_LAYER_PITCH:
token = LefNextToken(f, TRUE);
sscanf(token, "%f", &fvalue);
if (lefl->lefClass == CLASS_ROUTE)
lefl->info.route.pitch = (int)roundf(fvalue / oscale);
LefEndStatement(f);
break;
case LEF_LAYER_DIRECTION:
token = LefNextToken(f, TRUE);
LefLower(token);
if (lefl->lefClass == CLASS_ROUTE)
lefl->info.route.hdirection = (token[0] == 'h') ? TRUE : FALSE;
LefEndStatement(f);
break;
case LEF_LAYER_OFFSET:
case LEF_LAYER_RES:
case LEF_LAYER_CAP:
case LEF_LAYER_EDGECAP:
case LEF_LAYER_THICKNESS:
case LEF_LAYER_HEIGHT:
case LEF_LAYER_MINDENSITY:
case LEF_LAYER_ANTENNAAREA:
case LEF_LAYER_ANTENNADIFF:
case LEF_LAYER_ANTENNASIDE:
case LEF_LAYER_WIREEXT:
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:
LefAddViaGeometry(f, lefl, curlayer, oscale);
LefEndStatement(f);
break;
case LEF_VIA_FOREIGN:
case LEF_VIARULE_VIA:
case LEF_VIA_ENCLOSURE:
case LEF_VIA_PREFERENCLOSURE:
case LEF_VIARULE_OVERHANG:
case LEF_VIARULE_METALOVERHANG:
LefEndStatement(f);
break;
case LEF_LAYER_END:
if (LefParseEndStatement(f, lname) == 0)
{
LefError(LEF_ERROR, "Layer END statement missing.\n");
keyword = -1;
}
break;
}
if (keyword == LEF_LAYER_END) break;
}
}
/*
*------------------------------------------------------------
*
* LefRead --
*
* Read a .lef file into a magic layout.
*
* Results:
* None.
*
* Side Effects:
* Many. Cell definitions and uses are created and added to
* the database.
*
*------------------------------------------------------------
*/
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_NONDEFAULTRULE,
LEF_NOWIREEXTENSIONATPIN, LEF_SECTION_SPACING, LEF_SECTION_SITE,
LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP,
LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO,
LEF_END};
void
LefRead(inName, importForeign, doAnnotate, lefTimestamp)
char *inName;
bool importForeign;
bool doAnnotate;
int lefTimestamp;
{
FILE *f;
char *filename;
char *token;
char tsave[128];
int keyword;
float oscale;
HashEntry *he;
lefLayer *lefl;
static char *sections[] = {
"VERSION",
"BUSBITCHARS",
"DIVIDERCHAR",
"MANUFACTURINGGRID",
"USEMINSPACING",
"CLEARANCEMEASURE",
"NAMESCASESENSITIVE",
"PROPERTYDEFINITIONS",
"UNITS",
"LAYER",
"VIA",
"VIARULE",
"NONDEFAULTRULE",
"NOWIREEXTENSIONATPIN",
"SPACING",
"SITE",
"NOISETABLE",
"CORRECTIONTABLE",
"IRDROP",
"ARRAY",
"TIMING",
"BEGINEXT",
"MACRO",
"END",
NULL
};
/* Make sure we have a valid LefInfo hash table, even if it's empty */
if (LefInfo.ht_table == (HashEntry **) NULL)
LefTechInit();
f = lefFileOpen(NULL, inName, ".lef", "r", &filename);
if (f == NULL)
{
#ifdef MAGIC_WRAPPER
TxError("Cannot open input file %s (%s).\n", filename,
strerror(errno));
#else
TxError("Cannot open input file: ");
perror(filename);
#endif
return;
}
TxPrintf("Reading LEF data from file %s.\n", filename);
TxPrintf("This action cannot be undone.\n");
UndoDisable();
/* Initialize */
HashInit(&LefCellTable, 32, HT_STRINGKEYS);
HashInit(&lefDefInitHash, 32, HT_STRINGKEYS);
oscale = CIFGetOutputScale(1000);
lefCurrentLine = 0;
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = Lookup(token, sections);
if (keyword < 0)
{
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
token);
LefEndStatement(f);
continue;
}
switch (keyword)
{
case LEF_VERSION:
case LEF_BUSBITCHARS:
case LEF_DIVIDERCHAR:
case LEF_CLEARANCEMEASURE:
case LEF_MANUFACTURINGGRID:
case LEF_USEMINSPACING:
LefEndStatement(f);
break;
case LEF_NAMESCASESENSITIVE:
case LEF_NOWIREEXTENSIONATPIN:
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);
he = HashFind(&LefInfo, token);
lefl = (lefLayer *)HashGetValue(he);
if (lefl == NULL)
{
lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
lefl->type = -1;
lefl->obsType = -1;
lefl->refCnt = 1;
lefl->lefClass = CLASS_VIA;
lefl->info.via.area = GeoNullRect;
lefl->info.via.cell = (CellDef *)NULL;
lefl->info.via.lr = (LinkedRect *)NULL;
HashSetValue(he, lefl);
LefReadLayerSection(f, tsave, keyword, lefl);
lefl->canonName = (char *)he->h_key.h_name;
}
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(LEF_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);
he = HashLookOnly(&LefInfo, token);
if (he == NULL)
{
TileType mtype = DBTechNameType(token);
if (mtype < 0)
mtype = DBTechNameType(LefLower(token));
if (mtype < 0)
{
/* Ignore. This is probably a masterslice or */
/* overlap, but that hasn't been parsed yet. */
LefSkipSection(f, tsave);
break;
}
else if (DBIsContact(mtype) && (keyword == LEF_SECTION_LAYER))
{
LefError(LEF_ERROR, "Layer %s maps to a magic contact layer; "
"must be defined in lef section of techfile\n",
token);
LefSkipSection(f, tsave);
break;
}
else if (!DBIsContact(mtype) && (keyword != LEF_SECTION_LAYER))
{
LefError(LEF_ERROR, "Via %s maps to a non-contact magic layer; "
"must be defined in lef section of techfile\n",
token);
LefSkipSection(f, tsave);
break;
}
else
{
he = HashFind(&LefInfo, token);
lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
lefl->type = mtype;
lefl->obsType = -1;
lefl->refCnt = 1;
lefl->lefClass = (DBIsContact(mtype)) ? CLASS_VIA : CLASS_ROUTE;
HashSetValue(he, lefl);
lefl->canonName = (char *)he->h_key.h_name;
}
}
else
{
lefl = (lefLayer *)HashGetValue(he);
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_NONDEFAULTRULE:
token = LefNextToken(f, TRUE);
sprintf(tsave, "%.127s", token);
LefReadNonDefaultRule(f, tsave, oscale);
break;
case LEF_SECTION_SPACING:
LefSkipSection(f, sections[LEF_SECTION_SPACING]);
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_SECTION_SITE:
case LEF_MACRO:
token = LefNextToken(f, TRUE);
/* Diagnostic */
/*
TxPrintf("LEF file: Defines new cell %s\n", token);
*/
sprintf(tsave, "%.127s", token);
LefReadMacro(f, tsave, oscale, importForeign, doAnnotate, lefTimestamp);
break;
case LEF_END:
if (LefParseEndStatement(f, "LIBRARY") == 0)
{
LefError(LEF_ERROR, "END statement out of context.\n");
keyword = -1;
}
break;
}
if (keyword == LEF_END) break;
}
TxPrintf("LEF read: Processed %d lines.\n", lefCurrentLine);
LefError(LEF_SUMMARY, NULL); /* print statement of errors, if any */
/* Cleanup */
HashKill(&LefCellTable);
HashKill(&lefDefInitHash);
if (f != NULL) fclose(f);
UndoEnable();
}