magic/lef/lefRead.c

3209 lines
79 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 const 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 <strings.h>
#include <errno.h>
#include <stdarg.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#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(
int processed,
int total,
const char *item_name)
{
static struct timeval tv_start;
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(
int processed,
int total,
const 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.
*
*------------------------------------------------------------
*/
const char *
LefNextToken(
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 const 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,
const char *fmt, ...)
{
static int errors = 0, warnings = 0, messages = 0;
va_list args;
const char *lefdeftypes[] = {"LEF", "DEF", "techfile lef section"};
int mode, level;
const 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;
default:
ASSERT(FALSE, "type");
return;
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(
FILE *f,
const char *match)
{
const char *token;
int keyword;
const char * match_name[2];
static const char * const 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(
FILE *f,
const char *section)
{
const char *token;
int keyword, result;
static const char * const end_section[] = {
"END",
"ENDEXT",
NULL
};
while ((token = LefNextToken(f, TRUE)) != NULL)
{
if ((keyword = LookupFull(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(
const 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(
char *token)
{
char *tptr;
for (tptr = token; *tptr != '\0'; tptr++)
*tptr = tolower(*tptr);
return token;
}
/*
*------------------------------------------------------------
* LefHelper_DBTechNameType_LefLower --
*
* Helper function to make other parts of module code more
* readable (one liner usage), for the common use of:
* tileType = DBTechNameType(LefLower(token));
*
* This function allows token to be a (const char *) and
* original data source is not edited.
*
* Results:
* as per TileType from DBTechNameType() after lookup.
*
* Side Effects:
: None
*
* Module internal API only.
*/
TileType
LefHelper_DBTechNameType_LefLower(const char *name)
{
char tmp[256];
strncpy(tmp, name, sizeof(tmp));
/* after maybe helps codegen, only care to check overrun */
ASSERT(strlen(name) < sizeof(tmp), "strlen(name)");
tmp[sizeof(tmp)-1] = '\0';
LefLower(tmp);
return DBTechNameType(tmp);
}
/*
*------------------------------------------------------------
* 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(
lefLayer *lefl,
const char *redefname)
{
lefLayer *slef, *newlefl;
const 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((const char *)he->h_key.h_name, redefname))
altName = (const 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 = (const 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(
FILE *f,
bool obstruct,
TileType *lreturn,
const Rect **rreturn)
{
const 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)
{
curlayer = LefHelper_DBTechNameType_LefLower(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(
FILE *f,
bool obstruct)
{
return LefReadLayers(f, obstruct, (TileType *)NULL, (const 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. Return -1 if no
* coordinates are found in the input (used where
* coordinates may be optional).
*
* 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(
FILE *f,
float *xp,
float *yp)
{
const char *token;
bool needMatch = FALSE;
token = LefNextToken(f, TRUE);
if (!token) return -1;
if (*token == ';') return -1;
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(
FILE *f,
TileType curlayer,
float oscale)
{
const 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(
FILE *f,
TileType curlayer,
float oscale,
Point *gdsOffset,
int *ppoints)
{
LinkedRect *lr = NULL, *newRect;
Point *plist = NULL;
const 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) + gdsOffset->p_x;
newRect->r_r.r_ybot = (int)roundf(py / oscale) + gdsOffset->p_y;
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(
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 *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 *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(
CellDef *lefMacro,
FILE *f,
float oscale,
Point *gdsOffset,
bool do_list,
bool is_imported)
{
TileType curlayer = -1, otherlayer = -1;
const char *token;
int keyword;
LinkedRect *newRect, *rectList;
Point *pointList;
int points;
Rect *paintrect;
const Rect *contact = NULL;
static const char * const geometry_keys[] = {
"LAYER",
"WIDTH",
"PATH",
"RECT",
"POLYGON",
"VIA",
"CLASS",
"END",
NULL
};
rectList = NULL;
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = LookupFull(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)
{
paintrect->r_xbot += gdsOffset->p_x;
paintrect->r_ybot += gdsOffset->p_y;
paintrect->r_xtop += gdsOffset->p_x;
paintrect->r_ytop += gdsOffset->p_y;
if (is_imported)
{
int pNum = DBPlane(curlayer); /* FIXME unused return value from call to function with no side-effects */
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, gdsOffset, &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, FALSE);
}
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(
CellDef *lefMacro,
FILE *f,
char *pinName,
int pinNum,
int pinDir,
int pinUse,
int pinShape,
float oscale,
Point *gdsOffset,
bool is_imported,
Label *lanno)
{
Label *newlab;
LinkedRect *rectList;
rectList = LefReadGeometry(lefMacro, f, oscale, gdsOffset, 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
{
/* If any other label is a port and has the same name, */
/* then use its port number. */
Label *sl;
for (sl = lefMacro->cd_labels; sl != NULL; sl = sl->lab_next)
{
if (sl->lab_flags & PORT_DIR_MASK)
if (!strcmp(sl->lab_text, pinName))
{
pinNum = sl->lab_port;
break;
}
}
/* 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(
CellDef *lefMacro,
FILE *f,
char *pinname,
int pinNum,
float oscale,
Point *gdsOffset,
bool is_imported)
{
const 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 const char * const pin_keys[] = {
"DIRECTION",
"USE",
"PORT",
"CAPACITANCE",
"ANTENNADIFFAREA",
"ANTENNAGATEAREA",
"ANTENNAMODEL",
"ANTENNAPARTIALMETALAREA",
"ANTENNAPARTIALMETALSIDEAREA",
"ANTENNAPARTIALCUTAREA",
"ANTENNAMAXAREACAR",
"ANTENNAMAXSIDEAREACAR",
"SHAPE",
"NETEXPR",
"END",
NULL
};
static const char * const pin_classes[] = {
"DEFAULT",
"INPUT",
"OUTPUT",
"OUTPUT TRISTATE",
"INOUT",
"FEEDTHRU",
NULL
};
static const int lef_class_to_bitmask[] = {
PORT_CLASS_DEFAULT,
PORT_CLASS_INPUT,
PORT_CLASS_OUTPUT,
PORT_CLASS_TRISTATE,
PORT_CLASS_BIDIRECTIONAL,
PORT_CLASS_FEEDTHROUGH
};
static const char * const pin_uses[] = {
"DEFAULT",
"SIGNAL",
"ANALOG",
"POWER",
"GROUND",
"CLOCK",
NULL
};
static const int lef_use_to_bitmask[] = {
PORT_USE_DEFAULT,
PORT_USE_SIGNAL,
PORT_USE_ANALOG,
PORT_USE_POWER,
PORT_USE_GROUND,
PORT_USE_CLOCK
};
static const char * const pin_shapes[] = {
"DEFAULT",
"ABUTMENT",
"RING",
"FEEDTHRU",
NULL
};
static const 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 = LookupFull(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 = LookupFull(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 = LookupFull(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 = LookupFull(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, gdsOffset, TRUE, lab);
}
else
LefSkipSection(f, NULL);
firstport = FALSE;
}
else
LefReadPort(lefMacro, f, testpin, pinNum, pinDir, pinUse,
pinShape, oscale, gdsOffset, 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(
FILE *f)
{
const 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(
FILE *f, /* LEF file being read */
char *rname, /* name of the rule */
float oscale) /* scale factor um->magic units */
{
const 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 const char * const 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 = LookupFull(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 = (const 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(
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;
const char *token;
char tsave[128], *propval;
int keyword, pinNum, propsize, result;
float x, y;
bool has_size, is_imported = FALSE, propfound;
Rect lefBBox;
Point gdsOffset; /* Difference between GDS and LEF coordinates */
static const char * const 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;
gdsOffset.p_x = 0;
gdsOffset.p_y = 0;
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = LookupFull(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;
}
gdsOffset.p_x += lefBBox.r_xbot;
gdsOffset.p_y += 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, &gdsOffset, is_imported);
break;
case LEF_OBS:
/* Diagnostic */
/*
TxPrintf(" Macro defines obstruction\n");
*/
if (is_imported)
LefSkipSection(f, NULL);
else
LefReadGeometry(lefMacro, f, oscale, &gdsOffset, FALSE, is_imported);
break;
case LEF_TIMING:
LefSkipSection(f, macro_keys[LEF_TIMING]);
break;
case LEF_FOREIGN:
token = LefNextToken(f, TRUE);
sprintf(tsave, "%.127s", token);
/* Read (optional) FOREIGN coordinate */
result = LefReadLefPoint(f, &x, &y);
if (result == 1) goto foreign_error;
else if (result == 0)
{
gdsOffset.p_x += -(int)roundf(x / oscale);
gdsOffset.p_y += -(int)roundf(y / oscale);
}
if (importForeign)
{
/* There is no behavioral difference when using
* importForiegn.
*/
}
LefEndStatement(f);
break;
foreign_error:
LefError(LEF_ERROR, "Bad origin in macro FOREIGN; requires "
"0 or 2 values.\n");
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 */
lefBBox.r_xbot -= gdsOffset.p_x;
lefBBox.r_ybot -= gdsOffset.p_y;
lefBBox.r_xtop -= gdsOffset.p_x;
lefBBox.r_ytop -= gdsOffset.p_y;
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);
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(
FILE *f, /* LEF file being read */
lefLayer *lefl, /* pointer to via info */
int sizex, /* cut size */
int sizey,
int spacex, /* cut spacing */
int spacey,
int encbx, /* bottom enclosure of cuts */
int encby,
int enctx, /* top enclosure of cuts */
int encty,
int rows, /* number of cut rows and columns */
int cols,
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;
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(
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(
FILE *f, /* LEF file being read */
const char *lname, /* name of the layer */
int mode, /* layer, via, or viarule */
lefLayer *lefl) /* pointer to layer info */
{
const char *token;
int keyword, typekey;
TileType curlayer = -1;
float fvalue, oscale;
/* These are defined in the order of CLASS_* in lefInt.h */
static const char * const layer_type_keys[] = {
"ROUTING",
"CUT",
"MASTERSLICE",
"OVERLAP",
NULL
};
static const char * const 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 const char * const spacing_keys[] = {
"RANGE",
";",
NULL
};
oscale = CIFGetOutputScale(1000);
while ((token = LefNextToken(f, TRUE)) != NULL)
{
keyword = LookupFull(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 = LookupFull(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);
if (lefl->lefClass == CLASS_ROUTE)
lefl->info.route.hdirection = (tolower(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(
const char *inName,
bool importForeign,
bool doAnnotate,
int lefTimestamp)
{
FILE *f;
char *filename;
const char *token;
char tsave[128];
int keyword;
float oscale;
HashEntry *he;
lefLayer *lefl;
static const char * const 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 = LookupFull(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 = (const 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 = LefHelper_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 = (const 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();
}