2017-04-25 14:41:48 +02:00
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* lefRead.c --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
2025-01-04 10:01:41 +01:00
|
|
|
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 $";
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <string.h>
|
2024-10-04 19:59:39 +02:00
|
|
|
#include <strings.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdarg.h>
|
2024-10-12 14:57:08 +02:00
|
|
|
#ifdef HAVE_SYS_TIME_H
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <sys/time.h>
|
2024-10-12 14:57:08 +02:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
#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"
|
2019-07-17 16:20:52 +02:00
|
|
|
#include "utils/utils.h" /* For StrDup() */
|
2017-04-25 14:41:48 +02:00
|
|
|
#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. . . */
|
2018-06-21 18:50:31 +02:00
|
|
|
#include "cif/CIFread.h" /* Access to cifCurReadStyle. . . */
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "lef/lefInt.h"
|
|
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "utils/signals.h"
|
|
|
|
|
#include "utils/signals.h"
|
|
|
|
|
#include "drc/drc.h"
|
|
|
|
|
#include "lef/lef.h"
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* ---------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefEstimate(
|
|
|
|
|
int processed,
|
|
|
|
|
int total,
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *item_name)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefEstimate(
|
|
|
|
|
int processed,
|
|
|
|
|
int total,
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *item_name)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *
|
2025-01-04 09:50:40 +01:00
|
|
|
LefNextToken(
|
|
|
|
|
FILE *f,
|
|
|
|
|
bool ignore_eol)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
static char line[LEF_LINE_MAX + 2]; /* input buffer */
|
|
|
|
|
static char *nexttoken = NULL; /* pointer to next token */
|
|
|
|
|
static char *curtoken; /* pointer to current token */
|
2025-02-18 14:45:36 +01:00
|
|
|
static const char eol_token='\n';
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
2019-06-06 15:59:56 +02:00
|
|
|
* LefDefError --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Print an error message (via TxError) giving the line
|
|
|
|
|
* number of the input file on which the error occurred.
|
|
|
|
|
*
|
2019-06-06 15:59:56 +02:00
|
|
|
* "type" is one of the following (see lef.h):
|
|
|
|
|
* LEF_ERROR, LEF_WARNING, LEF_INFO,
|
|
|
|
|
* DEF_ERROR, DEF_WARNING, DEF_INFO
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Prints to the output (stderr).
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
LefError(
|
|
|
|
|
int type,
|
|
|
|
|
const char *fmt, ...)
|
2020-05-23 23:13:14 +02:00
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
static int errors = 0, warnings = 0, messages = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
va_list args;
|
|
|
|
|
|
2025-02-18 14:52:14 +01:00
|
|
|
const char *lefdeftypes[] = {"LEF", "DEF", "techfile lef section"};
|
2019-06-06 15:59:56 +02:00
|
|
|
|
|
|
|
|
int mode, level;
|
2025-02-18 14:52:14 +01:00
|
|
|
const char *lefdeftype;
|
2019-06-06 15:59:56 +02:00
|
|
|
|
|
|
|
|
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;
|
2024-10-04 21:29:15 +02:00
|
|
|
default:
|
|
|
|
|
ASSERT(FALSE, "type");
|
|
|
|
|
return;
|
|
|
|
|
break;
|
2019-06-06 15:59:56 +02:00
|
|
|
}
|
|
|
|
|
lefdeftype = lefdeftypes[mode];
|
|
|
|
|
|
|
|
|
|
if ((fmt == NULL) || (level == -1))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
/* Special case: report any errors and reset */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (errors)
|
2019-06-06 15:59:56 +02:00
|
|
|
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;
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-06 15:59:56 +02:00
|
|
|
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;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-06-06 15:59:56 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefParseEndStatement(
|
|
|
|
|
FILE *f,
|
|
|
|
|
const char *match)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2025-01-04 10:03:33 +01:00
|
|
|
int keyword;
|
2024-10-10 21:16:01 +02:00
|
|
|
const char * match_name[2];
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const end_section[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"END",
|
|
|
|
|
"ENDEXT",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match_name[0] = match;
|
|
|
|
|
match_name[1] = NULL;
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, (match == NULL) ? FALSE : TRUE);
|
|
|
|
|
if (token == NULL)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Bad file read while looking for END statement\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
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:
|
2019-06-06 15:59:56 +02:00
|
|
|
* reads input from the specified file. Prints an
|
2017-04-25 14:41:48 +02:00
|
|
|
* error message if the expected END record cannot
|
|
|
|
|
* be found.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
LefSkipSection(
|
|
|
|
|
FILE *f,
|
|
|
|
|
const char *section)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-01-04 09:50:19 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
int keyword, result;
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const end_section[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"END",
|
|
|
|
|
"ENDEXT",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
if ((keyword = LookupFull(token, end_section)) == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
result = -1;
|
|
|
|
|
while (result == -1)
|
|
|
|
|
{
|
|
|
|
|
result = LefParseEndStatement(f, section);
|
|
|
|
|
if (result == 1) return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (keyword == 1)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(section, "BEGINEXT"))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Section %s has no END record!\n", section);
|
2017-04-25 14:41:48 +02:00
|
|
|
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 *
|
2025-01-04 09:50:40 +01:00
|
|
|
lefFindCell(
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *name) /* Name of the cell to search for */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
HashEntry *h;
|
|
|
|
|
CellDef *def;
|
|
|
|
|
|
|
|
|
|
h = HashFind(&LefCellTable, name);
|
|
|
|
|
if (HashGetValue(h) == 0)
|
|
|
|
|
{
|
|
|
|
|
def = DBCellLookDef(name);
|
|
|
|
|
if (def == NULL)
|
|
|
|
|
{
|
2020-03-21 17:40:35 +01:00
|
|
|
def = DBCellNewDef(name);
|
2017-04-25 14:41:48 +02:00
|
|
|
DBReComputeBbox(def);
|
|
|
|
|
}
|
|
|
|
|
HashSetValue(h, def);
|
|
|
|
|
}
|
|
|
|
|
return (CellDef *) HashGetValue(h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* LefLower --
|
|
|
|
|
*
|
|
|
|
|
* Convert a token in a LEF or DEF file to all-lowercase.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *
|
2025-01-04 09:50:40 +01:00
|
|
|
LefLower(
|
|
|
|
|
char *token)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
char *tptr;
|
|
|
|
|
|
|
|
|
|
for (tptr = token; *tptr != '\0'; tptr++)
|
|
|
|
|
*tptr = tolower(*tptr);
|
|
|
|
|
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 14:45:36 +01:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* 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.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Side Effects:
|
|
|
|
|
* May add an entry to the list of LEF layers.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
lefLayer *
|
2025-01-04 09:50:40 +01:00
|
|
|
LefRedefined(
|
|
|
|
|
lefLayer *lefl,
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *redefname)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
lefLayer *slef, *newlefl;
|
2025-01-04 10:05:56 +01:00
|
|
|
const char *altName;
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
2024-10-04 18:23:02 +02:00
|
|
|
while ((he = HashNext(&LefInfo, &hs)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
slef = (lefLayer *)HashGetValue(he);
|
|
|
|
|
if (slef == lefl)
|
|
|
|
|
records++;
|
|
|
|
|
if (altName == NULL)
|
2025-01-04 10:05:56 +01:00
|
|
|
if (strcmp((const char *)he->h_key.h_name, redefname))
|
|
|
|
|
altName = (const char *)he->h_key.h_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
if (records == 1)
|
|
|
|
|
{
|
|
|
|
|
/* Only one name associated with the record, so */
|
|
|
|
|
/* just clear all the allocated information. */
|
|
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
for (viaLR = lefl->info.via.lr; viaLR != NULL; viaLR = viaLR->r_next)
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, viaLR);
|
|
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
newlefl = lefl;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
he = HashFind(&LefInfo, redefname);
|
|
|
|
|
newlefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
|
|
|
|
|
newlefl->refCnt = 1;
|
2025-01-04 10:05:56 +01:00
|
|
|
newlefl->canonName = (const char *)he->h_key.h_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadLayers(
|
|
|
|
|
FILE *f,
|
|
|
|
|
bool obstruct,
|
|
|
|
|
TileType *lreturn,
|
|
|
|
|
const Rect **rreturn)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType curlayer = -1;
|
|
|
|
|
lefLayer *lefl = NULL;
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (*token == ';')
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Bad Layer statement\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
curlayer = LefHelper_DBTechNameType_LefLower(token);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE)))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
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");
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadLayer(
|
|
|
|
|
FILE *f,
|
|
|
|
|
bool obstruct)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-10-12 15:01:10 +02:00
|
|
|
return LefReadLayers(f, obstruct, (TileType *)NULL, (const Rect **)NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-10 18:21:04 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* LefReadLefPoint --
|
|
|
|
|
*
|
|
|
|
|
* Read a LEF point record from the file, and
|
|
|
|
|
* return the two coordinates in LEF units (floating-point).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
2025-03-29 20:46:23 +01:00
|
|
|
* Return 0 on success, 1 on error. Return -1 if no
|
|
|
|
|
* coordinates are found in the input (used where
|
|
|
|
|
* coordinates may be optional).
|
2020-05-10 18:21:04 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadLefPoint(
|
|
|
|
|
FILE *f,
|
|
|
|
|
float *xp,
|
|
|
|
|
float *yp)
|
2020-05-10 18:21:04 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2020-05-10 18:21:04 +02:00
|
|
|
bool needMatch = FALSE;
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2025-03-29 20:46:23 +01:00
|
|
|
if (!token) return -1;
|
2025-05-17 01:36:16 +02:00
|
|
|
if (*token == ';') return -1;
|
2020-05-10 18:21:04 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* 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 *
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadRect(
|
|
|
|
|
FILE *f,
|
|
|
|
|
TileType curlayer,
|
|
|
|
|
float oscale)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
float llx, lly, urx, ury;
|
|
|
|
|
static Rect paintrect;
|
2019-06-06 15:59:56 +02:00
|
|
|
Rect lefrect;
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2019-06-06 15:59:56 +02:00
|
|
|
{
|
|
|
|
|
LefError(LEF_ERROR, "No layer defined for RECT.\n");
|
|
|
|
|
paintrect.r_xbot = paintrect.r_ybot = 0;
|
|
|
|
|
paintrect.r_xtop = paintrect.r_ytop = 0;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Scale coordinates (microns to magic internal units) */
|
|
|
|
|
/* Need to scale grid if necessary! */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-06-06 15:59:56 +02:00
|
|
|
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);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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:
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Bad port geometry: RECT requires 4 values.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
return (Rect *)NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* LefReadPolygon --
|
|
|
|
|
*
|
|
|
|
|
* Read a LEF "POLYGON" record from the file, and
|
2020-05-23 23:13:14 +02:00
|
|
|
* return a linked point structure.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* 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 *
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadPolygon(
|
|
|
|
|
FILE *f,
|
|
|
|
|
TileType curlayer,
|
|
|
|
|
float oscale,
|
2025-03-29 20:46:23 +01:00
|
|
|
Point *gdsOffset,
|
2025-01-04 09:50:40 +01:00
|
|
|
int *ppoints)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
LinkedRect *lr = NULL, *newRect;
|
|
|
|
|
Point *plist = NULL;
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
float px, py;
|
|
|
|
|
int lpoints = 0;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (token == NULL || *token == ';') break;
|
|
|
|
|
if (sscanf(token, "%f", &px) != 1)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Bad X value in polygon.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (token == NULL || *token == ';')
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Missing Y value in polygon point!\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (sscanf(token, "%f", &py) != 1)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Bad Y value in polygon.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
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));
|
2025-03-29 20:46:23 +01:00
|
|
|
newRect->r_r.r_xbot = (int)roundf(px / oscale) + gdsOffset->p_x;
|
|
|
|
|
newRect->r_r.r_ybot = (int)roundf(py / oscale) + gdsOffset->p_y;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, lr);
|
2017-04-25 14:41:48 +02:00
|
|
|
lpoints++;
|
|
|
|
|
lr = lr->r_next;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2020-05-23 23:13:14 +02:00
|
|
|
return plist;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* LefPaintPolygon --
|
|
|
|
|
*
|
|
|
|
|
* Paint a polygon into the CellDef indicated by lefMacro.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
LinkedRect *
|
2025-01-04 09:50:40 +01:00
|
|
|
LefPaintPolygon(
|
|
|
|
|
CellDef *lefMacro,
|
|
|
|
|
Point *pointList,
|
|
|
|
|
int points,
|
|
|
|
|
TileType curlayer,
|
|
|
|
|
bool keep)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-12 20:49:42 +01:00
|
|
|
/* Structure used by lefConnectFunc(), below */
|
|
|
|
|
|
|
|
|
|
typedef struct _plane_type {
|
|
|
|
|
Plane *pt_plane;
|
|
|
|
|
TileType pt_type;
|
|
|
|
|
} PlaneType;
|
|
|
|
|
|
2022-11-02 14:40:20 +01:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
2022-12-12 20:49:42 +01:00
|
|
|
* lefConnectFunc --
|
2022-11-02 14:40:20 +01:00
|
|
|
*
|
|
|
|
|
* Callback function used by LefReadGeometry when doing LEF
|
2022-12-12 20:49:42 +01:00
|
|
|
* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefConnectFunc(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, // Not handled, but should be.
|
2025-01-04 09:50:40 +01:00
|
|
|
TreeContext *cxp)
|
2022-12-12 20:49:42 +01:00
|
|
|
{
|
|
|
|
|
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 --
|
2022-11-02 14:40:20 +01:00
|
|
|
*
|
2022-12-12 20:49:42 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
2022-11-02 14:40:20 +01:00
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
2022-12-12 20:49:42 +01:00
|
|
|
|
2022-11-02 14:40:20 +01:00
|
|
|
int
|
2025-01-04 09:50:40 +01:00
|
|
|
lefUnconnectFunc(
|
2026-01-03 02:12:37 +01:00
|
|
|
Tile *tile, /* (unused) */
|
|
|
|
|
TileType dinfo, /* (unused) */
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData clientdata) /* (unused) */
|
2022-11-02 14:40:20 +01:00
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* 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().
|
|
|
|
|
*
|
2022-11-02 14:40:20 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Side Effects:
|
|
|
|
|
* Reads input from file f;
|
|
|
|
|
* Paints into the CellDef lefMacro.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum lef_geometry_keys {LEF_LAYER = 0, LEF_WIDTH, LEF_PATH,
|
2018-02-02 20:16:39 +01:00
|
|
|
LEF_RECT, LEF_POLYGON, LEF_VIA, LEF_PORT_CLASS,
|
|
|
|
|
LEF_GEOMETRY_END};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
LinkedRect *
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadGeometry(
|
|
|
|
|
CellDef *lefMacro,
|
|
|
|
|
FILE *f,
|
|
|
|
|
float oscale,
|
2025-03-29 20:46:23 +01:00
|
|
|
Point *gdsOffset,
|
2025-01-04 09:50:40 +01:00
|
|
|
bool do_list,
|
|
|
|
|
bool is_imported)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TileType curlayer = -1, otherlayer = -1;
|
|
|
|
|
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
int keyword;
|
|
|
|
|
LinkedRect *newRect, *rectList;
|
|
|
|
|
Point *pointList;
|
|
|
|
|
int points;
|
2024-10-12 15:01:10 +02:00
|
|
|
Rect *paintrect;
|
|
|
|
|
const Rect *contact = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const geometry_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"LAYER",
|
|
|
|
|
"WIDTH",
|
|
|
|
|
"PATH",
|
|
|
|
|
"RECT",
|
|
|
|
|
"POLYGON",
|
|
|
|
|
"VIA",
|
2018-02-02 20:16:39 +01:00
|
|
|
"CLASS",
|
2017-04-25 14:41:48 +02:00
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
rectList = NULL;
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, geometry_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
|
|
|
|
|
token);
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2025-03-29 20:46:23 +01:00
|
|
|
paintrect->r_xbot += gdsOffset->p_x;
|
|
|
|
|
paintrect->r_ybot += gdsOffset->p_y;
|
|
|
|
|
paintrect->r_xtop += gdsOffset->p_x;
|
|
|
|
|
paintrect->r_ytop += gdsOffset->p_y;
|
|
|
|
|
|
2022-11-02 14:40:20 +01:00
|
|
|
if (is_imported)
|
|
|
|
|
{
|
2025-01-04 10:04:49 +01:00
|
|
|
int pNum = DBPlane(curlayer); /* FIXME unused return value from call to function with no side-effects */
|
2022-12-12 20:49:42 +01:00
|
|
|
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,
|
2022-11-29 23:02:36 +01:00
|
|
|
paintrect, &DBNotConnectTbl[curlayer],
|
2022-11-02 14:40:20 +01:00
|
|
|
lefUnconnectFunc, (ClientData)NULL) == 1)
|
|
|
|
|
{
|
|
|
|
|
LefEndStatement(f);
|
2022-12-12 20:49:42 +01:00
|
|
|
TiFreePlane(checkplane);
|
2022-11-02 14:40:20 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2022-12-12 20:49:42 +01:00
|
|
|
TiFreePlane(checkplane);
|
2022-11-02 14:40:20 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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
|
2019-04-01 17:26:00 +02:00
|
|
|
if (DBIsContact(curlayer) && contact && !(GEO_RECTNULL(contact)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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:
|
2025-03-29 20:46:23 +01:00
|
|
|
pointList = LefReadPolygon(f, curlayer, oscale, gdsOffset, &points);
|
2017-04-25 14:41:48 +02:00
|
|
|
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))
|
2024-10-04 19:42:06 +02:00
|
|
|
LefPaintPolygon(lefMacro, pointList, points, otherlayer, FALSE);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
freeMagic(pointList);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case LEF_VIA:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_PORT_CLASS:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case LEF_GEOMETRY_END:
|
|
|
|
|
if (LefParseEndStatement(f, NULL) == 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Geometry (PORT or OBS) END "
|
|
|
|
|
"statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2019-11-26 16:57:42 +01:00
|
|
|
* Generates a new label entry in the CellDef lefMacro.
|
|
|
|
|
* If "lanno" is not NULL, then the label pointed to by
|
2022-12-12 20:49:42 +01:00
|
|
|
* 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.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadPort(
|
|
|
|
|
CellDef *lefMacro,
|
|
|
|
|
FILE *f,
|
|
|
|
|
char *pinName,
|
|
|
|
|
int pinNum,
|
|
|
|
|
int pinDir,
|
|
|
|
|
int pinUse,
|
|
|
|
|
int pinShape,
|
|
|
|
|
float oscale,
|
2025-03-29 20:46:23 +01:00
|
|
|
Point *gdsOffset,
|
2025-01-04 09:50:40 +01:00
|
|
|
bool is_imported,
|
|
|
|
|
Label *lanno)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
Label *newlab;
|
|
|
|
|
LinkedRect *rectList;
|
|
|
|
|
|
2025-03-29 20:46:23 +01:00
|
|
|
rectList = LefReadGeometry(lefMacro, f, oscale, gdsOffset, TRUE, is_imported);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
while (rectList != NULL)
|
|
|
|
|
{
|
2020-05-26 23:58:05 +02:00
|
|
|
if ((pinNum >= 0) || (lanno != NULL))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* Label this area */
|
2019-11-26 16:57:42 +01:00
|
|
|
if (lanno != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Modify an existing label */
|
|
|
|
|
lanno->lab_rect = rectList->r_r;
|
|
|
|
|
lanno->lab_type = rectList->r_type;
|
2020-05-26 23:58:05 +02:00
|
|
|
|
|
|
|
|
/* 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)
|
2021-12-13 04:09:31 +01:00
|
|
|
pinNum = lanno->lab_port;
|
2020-05-26 23:58:05 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2021-12-13 04:09:31 +01:00
|
|
|
idx = sl->lab_port;
|
2020-05-26 23:58:05 +02:00
|
|
|
if (idx > pinNum) pinNum = idx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pinNum++;
|
|
|
|
|
}
|
2019-11-26 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
else
|
2024-03-18 21:25:49 +01:00
|
|
|
{
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-26 16:57:42 +01:00
|
|
|
/* Create a new label (non-rendered) */
|
2021-12-13 04:09:31 +01:00
|
|
|
DBPutLabel(lefMacro, &rectList->r_r, -1, pinName, rectList->r_type, 0, 0);
|
2024-03-18 21:25:49 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Set this label to be a port */
|
|
|
|
|
|
|
|
|
|
if (lefMacro->cd_labels == NULL)
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Internal error: No labels in cell!\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
{
|
2019-11-26 16:57:42 +01:00
|
|
|
newlab = (lanno != NULL) ? lanno : lefMacro->cd_lastLabel;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (strcmp(newlab->lab_text, pinName))
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Internal error: Can't find the label!\n");
|
2020-05-21 19:47:14 +02:00
|
|
|
else
|
2021-12-22 18:08:34 +01:00
|
|
|
{
|
2020-05-21 19:47:14 +02:00
|
|
|
/* 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 */
|
2021-12-22 18:08:34 +01:00
|
|
|
newlab->lab_flags = pinUse | pinDir | pinShape |
|
2020-05-29 04:06:22 +02:00
|
|
|
PORT_DIR_MASK | LABEL_STICKY;
|
2021-12-22 18:08:34 +01:00
|
|
|
newlab->lab_port = pinNum;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-11-26 16:57:42 +01:00
|
|
|
/* 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;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, (char *)rectList);
|
2017-04-25 14:41:48 +02:00
|
|
|
rectList = rectList->r_next;
|
|
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* 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,
|
2018-02-02 20:16:39 +01:00
|
|
|
LEF_ANTENNADIFF, LEF_ANTENNAGATE, LEF_ANTENNAMOD,
|
2018-07-19 16:23:30 +02:00
|
|
|
LEF_ANTENNAPAR, LEF_ANTENNAPARSIDE, LEF_ANTENNAPARCUT,
|
|
|
|
|
LEF_ANTENNAMAX, LEF_ANTENNAMAXSIDE,
|
2018-02-02 20:16:39 +01:00
|
|
|
LEF_SHAPE, LEF_NETEXPR, LEF_PIN_END};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadPin(
|
|
|
|
|
CellDef *lefMacro,
|
|
|
|
|
FILE *f,
|
|
|
|
|
char *pinname,
|
|
|
|
|
int pinNum,
|
|
|
|
|
float oscale,
|
2025-03-29 20:46:23 +01:00
|
|
|
Point *gdsOffset,
|
2025-01-04 09:50:40 +01:00
|
|
|
bool is_imported)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2020-05-29 18:37:10 +02:00
|
|
|
char *testpin = pinname;
|
2017-04-25 14:41:48 +02:00
|
|
|
int keyword, subkey;
|
|
|
|
|
int pinDir = PORT_CLASS_DEFAULT;
|
|
|
|
|
int pinUse = PORT_USE_DEFAULT;
|
2020-05-29 04:06:22 +02:00
|
|
|
int pinShape = PORT_SHAPE_DEFAULT;
|
2020-05-29 18:37:10 +02:00
|
|
|
Label *firstlab;
|
2020-11-23 20:28:46 +01:00
|
|
|
bool firstport = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"DIRECTION",
|
|
|
|
|
"USE",
|
|
|
|
|
"PORT",
|
|
|
|
|
"CAPACITANCE",
|
2018-02-02 20:16:39 +01:00
|
|
|
"ANTENNADIFFAREA",
|
|
|
|
|
"ANTENNAGATEAREA",
|
|
|
|
|
"ANTENNAMODEL",
|
|
|
|
|
"ANTENNAPARTIALMETALAREA",
|
|
|
|
|
"ANTENNAPARTIALMETALSIDEAREA",
|
2018-07-19 16:23:30 +02:00
|
|
|
"ANTENNAPARTIALCUTAREA",
|
2018-02-02 20:16:39 +01:00
|
|
|
"ANTENNAMAXAREACAR",
|
|
|
|
|
"ANTENNAMAXSIDEAREACAR",
|
|
|
|
|
"SHAPE",
|
|
|
|
|
"NETEXPR",
|
2017-04-25 14:41:48 +02:00
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_classes[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"DEFAULT",
|
|
|
|
|
"INPUT",
|
|
|
|
|
"OUTPUT",
|
|
|
|
|
"OUTPUT TRISTATE",
|
|
|
|
|
"INOUT",
|
|
|
|
|
"FEEDTHRU",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const int lef_class_to_bitmask[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
PORT_CLASS_DEFAULT,
|
|
|
|
|
PORT_CLASS_INPUT,
|
|
|
|
|
PORT_CLASS_OUTPUT,
|
|
|
|
|
PORT_CLASS_TRISTATE,
|
|
|
|
|
PORT_CLASS_BIDIRECTIONAL,
|
|
|
|
|
PORT_CLASS_FEEDTHROUGH
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_uses[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"DEFAULT",
|
|
|
|
|
"SIGNAL",
|
|
|
|
|
"ANALOG",
|
|
|
|
|
"POWER",
|
|
|
|
|
"GROUND",
|
|
|
|
|
"CLOCK",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const int lef_use_to_bitmask[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
PORT_USE_DEFAULT,
|
|
|
|
|
PORT_USE_SIGNAL,
|
|
|
|
|
PORT_USE_ANALOG,
|
|
|
|
|
PORT_USE_POWER,
|
|
|
|
|
PORT_USE_GROUND,
|
|
|
|
|
PORT_USE_CLOCK
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_shapes[] = {
|
2020-05-29 04:06:22 +02:00
|
|
|
"DEFAULT",
|
|
|
|
|
"ABUTMENT",
|
|
|
|
|
"RING",
|
|
|
|
|
"FEEDTHRU",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const int lef_shape_to_bitmask[] = {
|
2020-05-29 04:06:22 +02:00
|
|
|
PORT_SHAPE_DEFAULT,
|
|
|
|
|
PORT_SHAPE_ABUT,
|
|
|
|
|
PORT_SHAPE_RING,
|
|
|
|
|
PORT_SHAPE_THRU
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-29 18:37:10 +02:00
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2020-05-29 18:51:21 +02:00
|
|
|
char *delim2;
|
2020-05-29 18:37:10 +02:00
|
|
|
*delim = '<';
|
2020-05-29 18:51:21 +02:00
|
|
|
if ((delim2 = strchr(testpin, ']')) != NULL)
|
|
|
|
|
*delim2 = '>';
|
2020-05-29 18:37:10 +02:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, pin_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
|
|
|
|
|
token);
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case LEF_DIRECTION:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, pin_classes);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subkey < 0)
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Improper DIRECTION statement\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
pinDir = lef_class_to_bitmask[subkey];
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_USE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, pin_uses);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subkey < 0)
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Improper USE statement\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
pinUse = lef_use_to_bitmask[subkey];
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2020-05-29 04:06:22 +02:00
|
|
|
case LEF_SHAPE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, pin_shapes);
|
2020-05-29 04:06:22 +02:00
|
|
|
if (subkey < 0)
|
|
|
|
|
LefError(LEF_ERROR, "Improper SHAPE statement\n");
|
|
|
|
|
else
|
|
|
|
|
pinShape = lef_shape_to_bitmask[subkey];
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case LEF_PORT:
|
2019-04-01 17:26:00 +02:00
|
|
|
if (is_imported)
|
|
|
|
|
{
|
2019-11-26 16:57:42 +01:00
|
|
|
bool needRect = TRUE;
|
2020-09-26 04:39:50 +02:00
|
|
|
bool hasPort = FALSE;
|
2019-04-01 17:26:00 +02:00
|
|
|
Label *lab;
|
|
|
|
|
|
2020-09-26 04:39:50 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-01 17:26:00 +02:00
|
|
|
/* 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. */
|
2019-11-26 16:57:42 +01:00
|
|
|
/* However, if the label is a point label, then */
|
|
|
|
|
/* replace it with the geometry from the LEF file. */
|
2020-05-27 02:40:41 +02:00
|
|
|
|
2020-09-26 04:39:50 +02:00
|
|
|
if (hasPort == FALSE) lab = firstlab;
|
|
|
|
|
for (; lab; lab = lab->lab_next)
|
2019-04-01 17:26:00 +02:00
|
|
|
{
|
2020-05-29 18:37:10 +02:00
|
|
|
if (!strcmp(lab->lab_text, testpin))
|
2019-04-01 17:26:00 +02:00
|
|
|
{
|
2020-09-26 04:39:50 +02:00
|
|
|
/* 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))
|
2019-11-26 16:57:42 +01:00
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-27 02:40:41 +02:00
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
2021-12-13 04:09:31 +01:00
|
|
|
pinNum = lab->lab_port;
|
2020-05-27 02:40:41 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2021-12-13 04:09:31 +01:00
|
|
|
idx = sl->lab_port;
|
2020-05-27 02:40:41 +02:00
|
|
|
if (idx > pinNum) pinNum = idx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pinNum++;
|
|
|
|
|
}
|
2019-11-26 16:57:42 +01:00
|
|
|
needRect = FALSE;
|
|
|
|
|
lab->lab_flags &= ~(PORT_USE_MASK | PORT_DIR_MASK |
|
2020-05-29 04:06:22 +02:00
|
|
|
PORT_CLASS_MASK | PORT_SHAPE_MASK);
|
2021-12-22 18:08:34 +01:00
|
|
|
lab->lab_flags |= pinUse | pinDir | pinShape |
|
2019-11-26 16:57:42 +01:00
|
|
|
PORT_DIR_MASK;
|
2021-12-22 18:08:34 +01:00
|
|
|
lab->lab_port = pinNum;
|
2019-11-26 16:57:42 +01:00
|
|
|
}
|
2019-04-01 17:26:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-05-29 18:37:10 +02:00
|
|
|
firstlab = (lab == NULL) ? NULL : lab->lab_next;
|
|
|
|
|
|
2019-11-26 16:57:42 +01:00
|
|
|
if (needRect)
|
|
|
|
|
{
|
2020-11-23 20:28:46 +01:00
|
|
|
if ((lab == NULL) && (firstport == TRUE))
|
2020-05-29 18:37:10 +02:00
|
|
|
DBEraseLabelsByContent(lefMacro, NULL, -1, testpin);
|
2022-12-12 20:49:42 +01:00
|
|
|
|
2020-05-29 18:37:10 +02:00
|
|
|
LefReadPort(lefMacro, f, testpin, pinNum, pinDir, pinUse,
|
2025-03-29 20:46:23 +01:00
|
|
|
pinShape, oscale, gdsOffset, TRUE, lab);
|
2019-11-26 16:57:42 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
LefSkipSection(f, NULL);
|
2020-11-23 20:28:46 +01:00
|
|
|
firstport = FALSE;
|
2019-04-01 17:26:00 +02:00
|
|
|
}
|
|
|
|
|
else
|
2020-05-29 18:37:10 +02:00
|
|
|
LefReadPort(lefMacro, f, testpin, pinNum, pinDir, pinUse,
|
2025-03-29 20:46:23 +01:00
|
|
|
pinShape, oscale, gdsOffset, FALSE, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case LEF_CAPACITANCE:
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_ANTENNADIFF:
|
|
|
|
|
case LEF_ANTENNAGATE:
|
|
|
|
|
case LEF_ANTENNAMOD:
|
|
|
|
|
case LEF_ANTENNAPAR:
|
|
|
|
|
case LEF_ANTENNAPARSIDE:
|
2018-07-19 16:23:30 +02:00
|
|
|
case LEF_ANTENNAPARCUT:
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_ANTENNAMAX:
|
|
|
|
|
case LEF_ANTENNAMAXSIDE:
|
|
|
|
|
case LEF_NETEXPR:
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f); /* Ignore. . . */
|
|
|
|
|
break;
|
|
|
|
|
case LEF_PIN_END:
|
2020-05-29 19:10:03 +02:00
|
|
|
if (LefParseEndStatement(f, pinname) == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Pin END statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == LEF_PIN_END) break;
|
|
|
|
|
}
|
2020-05-29 19:10:03 +02:00
|
|
|
if (testpin != pinname) freeMagic(testpin);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* LefEndStatement --
|
|
|
|
|
*
|
|
|
|
|
* Read file input to EOF or a ';' token (end-of-statement)
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
LefEndStatement(
|
|
|
|
|
FILE *f)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
if (*token == ';') break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-31 16:23:39 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadNonDefaultRule(
|
|
|
|
|
FILE *f, /* LEF file being read */
|
|
|
|
|
char *rname, /* name of the rule */
|
|
|
|
|
float oscale) /* scale factor um->magic units */
|
2022-03-31 16:23:39 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2022-03-31 16:23:39 +02:00
|
|
|
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;
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const nondef_property_keys[] = {
|
2022-03-31 16:23:39 +02:00
|
|
|
"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)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, nondef_property_keys);
|
2022-03-31 16:23:39 +02:00
|
|
|
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);
|
2025-01-04 10:05:56 +01:00
|
|
|
lefl->canonName = (const char *)he->h_key.h_name;
|
2022-03-31 16:23:39 +02:00
|
|
|
}
|
|
|
|
|
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;
|
2022-11-09 17:15:06 +01:00
|
|
|
rule->extend = 0;
|
2022-03-31 16:23:39 +02:00
|
|
|
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);
|
2022-04-01 02:02:12 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2022-03-31 16:23:39 +02:00
|
|
|
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);
|
2022-04-01 02:02:12 +02:00
|
|
|
LefEndStatement(f);
|
2022-03-31 16:23:39 +02:00
|
|
|
break;
|
|
|
|
|
case LEF_NONDEFLAYER_EXT:
|
2022-11-09 17:15:06 +01:00
|
|
|
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:
|
2022-03-31 16:23:39 +02:00
|
|
|
if (!inlayer)
|
|
|
|
|
LefError(LEF_INFO,
|
|
|
|
|
"Layer value specified without layer.\n");
|
|
|
|
|
/* Absorb token and ignore */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2022-04-01 02:02:12 +02:00
|
|
|
LefEndStatement(f);
|
2022-03-31 16:23:39 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (done) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newname != rname) freeMagic(newname);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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,
|
2020-05-29 20:31:48 +02:00
|
|
|
LEF_TIMING, LEF_FOREIGN, LEF_PROPERTY, LEF_MACRO_END};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
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
|
2017-04-25 14:41:48 +02:00
|
|
|
* in a cell.
|
|
|
|
|
*/
|
2025-01-04 09:50:40 +01:00
|
|
|
bool doAnnotate, /* If true, ignore all macros that are
|
2020-10-14 23:20:45 +02:00
|
|
|
* not already CellDefs.
|
|
|
|
|
*/
|
2025-01-04 09:50:40 +01:00
|
|
|
int lefTimestamp) /* If not -1, use the value pointed to
|
2022-01-22 19:02:47 +01:00
|
|
|
* as the CellDef's timestamp.
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
CellDef *lefMacro;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
|
|
|
|
char tsave[128], *propval;
|
2025-03-29 20:46:23 +01:00
|
|
|
int keyword, pinNum, propsize, result;
|
2017-04-25 14:41:48 +02:00
|
|
|
float x, y;
|
2020-05-29 20:31:48 +02:00
|
|
|
bool has_size, is_imported = FALSE, propfound;
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
PropertyRecord *proprec;
|
2017-04-25 14:41:48 +02:00
|
|
|
Rect lefBBox;
|
2025-03-29 20:46:23 +01:00
|
|
|
Point gdsOffset; /* Difference between GDS and LEF coordinates */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const macro_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"CLASS",
|
|
|
|
|
"SIZE",
|
|
|
|
|
"ORIGIN",
|
|
|
|
|
"SYMMETRY",
|
|
|
|
|
"SOURCE",
|
|
|
|
|
"SITE",
|
|
|
|
|
"PIN",
|
|
|
|
|
"OBS",
|
|
|
|
|
"TIMING",
|
|
|
|
|
"FOREIGN",
|
2020-05-29 20:31:48 +02:00
|
|
|
"PROPERTY",
|
2017-04-25 14:41:48 +02:00
|
|
|
"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++)
|
|
|
|
|
{
|
2022-12-09 23:31:47 +01:00
|
|
|
snprintf(newname, 255, "%s_%d", mname, suffix);
|
2017-04-25 14:41:48 +02:00
|
|
|
he = HashFind(&lefDefInitHash, newname);
|
|
|
|
|
}
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_WARNING, "Cell \"%s\" was already defined in this file. "
|
2017-04-25 14:41:48 +02:00
|
|
|
"Renaming this cell \"%s\"\n", mname, newname);
|
|
|
|
|
lefMacro = DBCellLookDef(newname);
|
|
|
|
|
if (lefMacro == NULL)
|
|
|
|
|
{
|
2020-10-14 23:20:45 +02:00
|
|
|
if (doAnnotate)
|
|
|
|
|
{
|
|
|
|
|
/* Ignore any macro that does not correspond to an existing cell */
|
|
|
|
|
LefSkipSection(f, "MACRO");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2022-01-22 19:30:11 +01:00
|
|
|
if (lefTimestamp != -1)
|
2022-01-22 19:02:47 +01:00
|
|
|
{
|
2022-01-22 19:30:11 +01:00
|
|
|
lefMacro->cd_timestamp = lefTimestamp;
|
2022-01-22 20:35:33 +01:00
|
|
|
lefMacro->cd_flags |= CDFIXEDSTAMP;
|
2022-01-22 19:02:47 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Initial values */
|
|
|
|
|
pinNum = 1;
|
|
|
|
|
has_size = FALSE;
|
|
|
|
|
lefBBox.r_xbot = 0;
|
|
|
|
|
lefBBox.r_ybot = 0;
|
2025-03-29 20:46:23 +01:00
|
|
|
gdsOffset.p_x = 0;
|
|
|
|
|
gdsOffset.p_y = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, macro_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
|
|
|
|
|
token);
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case LEF_CLASS:
|
2020-03-30 15:36:24 +02:00
|
|
|
strcpy(tsave, "");
|
2017-04-25 14:41:48 +02:00
|
|
|
token = LefNextToken(f, TRUE);
|
2020-03-30 15:36:24 +02:00
|
|
|
while (*token != ';')
|
|
|
|
|
{
|
|
|
|
|
sprintf(tsave + strlen(tsave), " %s", token);
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
}
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
strlen(tsave + 1) - 7);
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_STRING;
|
|
|
|
|
proprec->prop_len = strlen(tsave + 1);
|
|
|
|
|
strcpy(proprec->prop_value.prop_string, tsave + 1);
|
|
|
|
|
DBPropPut(lefMacro, "LEFclass", proprec);
|
2017-04-25 14:41:48 +02:00
|
|
|
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:
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Bad macro SIZE; requires values X BY Y.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_ORIGIN:
|
2020-05-11 02:01:05 +02:00
|
|
|
if (LefReadLefPoint(f, &x, &y) != 0) goto origin_error;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2025-03-29 20:46:23 +01:00
|
|
|
gdsOffset.p_x += lefBBox.r_xbot;
|
|
|
|
|
gdsOffset.p_y += lefBBox.r_ybot;
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
origin_error:
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Bad macro ORIGIN; requires 2 values.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_SYMMETRY:
|
|
|
|
|
strcpy(tsave, "");
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
while (*token != ';')
|
|
|
|
|
{
|
|
|
|
|
sprintf(tsave + strlen(tsave), " %s", token);
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
}
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
strlen(tsave + 1) - 7);
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_STRING;
|
|
|
|
|
proprec->prop_len = strlen(tsave + 1);
|
|
|
|
|
strcpy(proprec->prop_value.prop_string, tsave + 1);
|
|
|
|
|
DBPropPut(lefMacro, "LEFsymmetry", proprec);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case LEF_SOURCE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2020-07-21 14:40:25 +02:00
|
|
|
/* Ignore "SOURCE" as it is deprecated from LEF 5.6, and */
|
|
|
|
|
/* magic will write LEF version 5.7. */
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_SITE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (*token != '\n')
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
{
|
|
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
strlen(token) - 7);
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_STRING;
|
|
|
|
|
proprec->prop_len = strlen(token);
|
|
|
|
|
strcpy(proprec->prop_value.prop_string, token);
|
|
|
|
|
DBPropPut(lefMacro, "LEFsite", proprec);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2020-05-29 20:31:48 +02:00
|
|
|
case LEF_PROPERTY:
|
|
|
|
|
/* Append property key:value pairs to the cell property LEFproperties */
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
propval = DBPropGetString(lefMacro, "LEFproperties", &propfound);
|
2020-05-29 20:31:48 +02:00
|
|
|
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);
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
|
|
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
propsize + strlen(tsave) + strlen(token) - 3);
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_STRING;
|
|
|
|
|
proprec->prop_len = propsize + strlen(tsave) + strlen(token) + 4;
|
|
|
|
|
|
2020-05-29 20:31:48 +02:00
|
|
|
if (propsize > 0)
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
sprintf(proprec->prop_value.prop_string, "%s %s %s",
|
|
|
|
|
propval, tsave, token);
|
2020-05-29 20:31:48 +02:00
|
|
|
else
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
sprintf(proprec->prop_value.prop_string, "%s %s", tsave, token);
|
|
|
|
|
|
|
|
|
|
DBPropPut(lefMacro, "LEFproperties", proprec);
|
2020-05-29 20:31:48 +02:00
|
|
|
}
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case LEF_PIN:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
/* Diagnostic */
|
|
|
|
|
/*
|
|
|
|
|
TxPrintf(" Macro defines pin %s\n", token);
|
|
|
|
|
*/
|
|
|
|
|
sprintf(tsave, "%.127s", token);
|
2025-03-29 20:46:23 +01:00
|
|
|
LefReadPin(lefMacro, f, tsave, pinNum++, oscale, &gdsOffset, is_imported);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case LEF_OBS:
|
|
|
|
|
/* Diagnostic */
|
|
|
|
|
/*
|
|
|
|
|
TxPrintf(" Macro defines obstruction\n");
|
|
|
|
|
*/
|
|
|
|
|
if (is_imported)
|
|
|
|
|
LefSkipSection(f, NULL);
|
|
|
|
|
else
|
2025-03-29 20:46:23 +01:00
|
|
|
LefReadGeometry(lefMacro, f, oscale, &gdsOffset, FALSE, is_imported);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case LEF_TIMING:
|
|
|
|
|
LefSkipSection(f, macro_keys[LEF_TIMING]);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_FOREIGN:
|
2025-03-29 20:46:23 +01:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
sprintf(tsave, "%.127s", token);
|
|
|
|
|
|
|
|
|
|
/* Read (optional) FOREIGN coordinate */
|
|
|
|
|
result = LefReadLefPoint(f, &x, &y);
|
2025-05-17 01:36:16 +02:00
|
|
|
if (result == 1) goto foreign_error;
|
2025-03-29 20:46:23 +01:00
|
|
|
else if (result == 0)
|
|
|
|
|
{
|
|
|
|
|
gdsOffset.p_x += -(int)roundf(x / oscale);
|
|
|
|
|
gdsOffset.p_y += -(int)roundf(y / oscale);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (importForeign)
|
|
|
|
|
{
|
2025-03-29 20:46:23 +01:00
|
|
|
/* There is no behavioral difference when using
|
|
|
|
|
* importForiegn.
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2025-08-20 21:01:57 +02:00
|
|
|
if (result != -1) LefEndStatement(f);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
2025-05-17 01:36:16 +02:00
|
|
|
foreign_error:
|
|
|
|
|
LefError(LEF_ERROR, "Bad origin in macro FOREIGN; requires "
|
|
|
|
|
"0 or 2 values.\n");
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case LEF_MACRO_END:
|
|
|
|
|
if (LefParseEndStatement(f, mname) == 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Macro END statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == LEF_MACRO_END) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finish up creating the cell */
|
2025-03-29 20:46:23 +01:00
|
|
|
lefBBox.r_xbot -= gdsOffset.p_x;
|
|
|
|
|
lefBBox.r_ybot -= gdsOffset.p_y;
|
|
|
|
|
lefBBox.r_xtop -= gdsOffset.p_x;
|
|
|
|
|
lefBBox.r_ytop -= gdsOffset.p_y;
|
2019-05-06 22:30:29 +02:00
|
|
|
DBReComputeBbox(lefMacro);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (is_imported)
|
|
|
|
|
{
|
2019-05-06 22:30:29 +02:00
|
|
|
/* Define the FIXED_BBOX property to match the LEF macro */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (has_size)
|
|
|
|
|
{
|
|
|
|
|
lefMacro->cd_flags |= CDFIXEDBBOX;
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
(2 * sizeof(int)));
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_DIMENSION;
|
|
|
|
|
proprec->prop_len = 4;
|
|
|
|
|
proprec->prop_value.prop_integer[0] = lefBBox.r_xbot;
|
|
|
|
|
proprec->prop_value.prop_integer[1] = lefBBox.r_ybot;
|
|
|
|
|
proprec->prop_value.prop_integer[2] = lefBBox.r_xtop;
|
|
|
|
|
proprec->prop_value.prop_integer[3] = lefBBox.r_ytop;
|
|
|
|
|
|
|
|
|
|
DBPropPut(lefMacro, "FIXED_BBOX", proprec);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-10-17 21:54:38 +02:00
|
|
|
DBAdjustLabelsNew(lefMacro, &TiPlaneRect);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-05-06 22:30:29 +02:00
|
|
|
if (has_size)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-05-06 22:30:29 +02:00
|
|
|
lefMacro->cd_flags |= CDFIXEDBBOX;
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
(2 * sizeof(int)));
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_DIMENSION;
|
|
|
|
|
proprec->prop_len = 4;
|
|
|
|
|
proprec->prop_value.prop_integer[0] = lefBBox.r_xbot;
|
|
|
|
|
proprec->prop_value.prop_integer[1] = lefBBox.r_ybot;
|
|
|
|
|
proprec->prop_value.prop_integer[2] = lefBBox.r_xtop;
|
|
|
|
|
proprec->prop_value.prop_integer[3] = lefBBox.r_ytop;
|
|
|
|
|
|
|
|
|
|
DBPropPut(lefMacro, "FIXED_BBOX", proprec);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_WARNING, " Macro does not define size: "
|
|
|
|
|
"computing from geometry\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-05-06 22:30:29 +02:00
|
|
|
/* Set the placement bounding box property to the current bounding box */
|
|
|
|
|
lefMacro->cd_flags |= CDFIXEDBBOX;
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
lefMacro->cd_flags |= CDFIXEDBBOX;
|
|
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
(2 * sizeof(int)));
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_DIMENSION;
|
|
|
|
|
proprec->prop_len = 4;
|
|
|
|
|
proprec->prop_value.prop_integer[0] = lefMacro->cd_bbox.r_xbot;
|
|
|
|
|
proprec->prop_value.prop_integer[1] = lefMacro->cd_bbox.r_ybot;
|
|
|
|
|
proprec->prop_value.prop_integer[2] = lefMacro->cd_bbox.r_xtop;
|
|
|
|
|
proprec->prop_value.prop_integer[3] = lefMacro->cd_bbox.r_ytop;
|
|
|
|
|
|
|
|
|
|
DBPropPut(lefMacro, "FIXED_BBOX", proprec);
|
2019-05-06 22:30:29 +02:00
|
|
|
DRCCheckThis(lefMacro, TT_CHECKPAINT, &lefMacro->cd_bbox);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
|
2019-06-05 23:02:20 +02:00
|
|
|
/* 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. */
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Note: The value here is ignored, setting to "TRUE". */
|
|
|
|
|
/* The "extract" command only cares that the key exists. */
|
2019-06-05 23:02:20 +02:00
|
|
|
/* i.e., setting it to "FALSE" would be ineffective. */
|
|
|
|
|
|
|
|
|
|
if (!is_imported)
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
{
|
|
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord));
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_STRING;
|
|
|
|
|
proprec->prop_len = 4;
|
|
|
|
|
strcpy(proprec->prop_value.prop_string, "TRUE");
|
|
|
|
|
DBPropPut(lefMacro, "LEFview", proprec);
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
DBWAreaChanged(lefMacro, &lefMacro->cd_bbox, DBW_ALLWINDOWS,
|
|
|
|
|
&DBAllButSpaceBits);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 20:24:52 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* LefGrowVia ---
|
|
|
|
|
*
|
|
|
|
|
* For LEF contact types matching magic contact types, size the
|
|
|
|
|
* LEF contact cut to cover the minimum rectangle in the other
|
2020-03-06 15:15:22 +01:00
|
|
|
* 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.
|
2019-07-03 20:24:52 +02:00
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void LefGrowVia(curlayer, currect, lefl)
|
|
|
|
|
TileType curlayer;
|
|
|
|
|
Rect *currect;
|
|
|
|
|
lefLayer *lefl;
|
|
|
|
|
{
|
2020-03-06 15:15:22 +01:00
|
|
|
/* To be completed: This should be deprecated by moving the entire */
|
|
|
|
|
/* LEF and DEF read routines to use the cifinput style. */
|
2019-07-03 20:24:52 +02:00
|
|
|
|
2020-03-06 15:15:22 +01:00
|
|
|
if (DBIsContact(curlayer) && CIFCurStyle != NULL)
|
2019-07-03 20:24:52 +02:00
|
|
|
{
|
|
|
|
|
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 */
|
2020-05-23 23:13:14 +02:00
|
|
|
contSize <<= 1;
|
|
|
|
|
edgeSize <<= 1;
|
2019-07-03 20:24:52 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
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 */
|
2019-07-03 19:58:13 +02:00
|
|
|
{
|
|
|
|
|
Rect rect;
|
2025-01-04 10:03:33 +01:00
|
|
|
int i, j, x, y, w, h;
|
2019-07-03 19:58:13 +02:00
|
|
|
LinkedRect *viaLR;
|
2019-07-03 20:05:01 +02:00
|
|
|
float hscale = oscale / 2;
|
2019-07-03 19:58:13 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
2019-07-03 20:52:22 +02:00
|
|
|
viaLR->r_type = blayer;
|
2019-07-03 19:58:13 +02:00
|
|
|
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++)
|
|
|
|
|
{
|
2019-07-03 20:05:01 +02:00
|
|
|
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);
|
2019-07-03 19:58:13 +02:00
|
|
|
|
2019-07-03 20:24:52 +02:00
|
|
|
/* Expand via to the size used by magic */
|
|
|
|
|
LefGrowVia(clayer, &rect, lefl);
|
|
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
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;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
y += sizey + spacey;
|
|
|
|
|
}
|
|
|
|
|
x += sizex + spacex;
|
|
|
|
|
y = -h / 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefAddViaGeometry(
|
|
|
|
|
FILE *f, /* LEF file being read */
|
|
|
|
|
lefLayer *lefl, /* pointer to via info */
|
|
|
|
|
TileType curlayer, /* current tile type */
|
|
|
|
|
float oscale) /* output scaling */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2019-07-03 20:24:52 +02:00
|
|
|
/* Expand via to the size used by magic */
|
|
|
|
|
LefGrowVia(curlayer, currect, lefl);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (GEO_SAMERECT(lefl->info.via.area, GeoNullRect))
|
|
|
|
|
{
|
|
|
|
|
lefl->info.via.area = *currect;
|
|
|
|
|
lefl->type = curlayer;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2018-02-02 20:16:39 +01:00
|
|
|
enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH,
|
|
|
|
|
LEF_LAYER_MAXWIDTH, LEF_LAYER_AREA,
|
|
|
|
|
LEF_LAYER_SPACING, LEF_LAYER_SPACINGTABLE,
|
2017-04-25 14:41:48 +02:00
|
|
|
LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET,
|
2018-02-02 20:16:39 +01:00
|
|
|
LEF_LAYER_WIREEXT,
|
|
|
|
|
LEF_LAYER_RES, LEF_LAYER_CAP, LEF_LAYER_EDGECAP,
|
|
|
|
|
LEF_LAYER_THICKNESS, LEF_LAYER_HEIGHT,
|
2019-02-14 18:19:50 +01:00
|
|
|
LEF_LAYER_PROPERTY, LEF_LAYER_ACDENSITY, LEF_LAYER_DCDENSITY,
|
2018-02-02 20:16:39 +01:00
|
|
|
LEF_LAYER_MINDENSITY, LEF_LAYER_ANTENNADIFF,
|
2018-07-19 16:23:30 +02:00
|
|
|
LEF_LAYER_ANTENNAAREA, LEF_LAYER_ANTENNASIDE,
|
2019-04-01 17:26:00 +02:00
|
|
|
LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT, LEF_VIA_FOREIGN,
|
2018-02-02 20:16:39 +01:00
|
|
|
LEF_VIA_ENCLOSURE, LEF_VIA_PREFERENCLOSURE,
|
|
|
|
|
LEF_VIARULE_OVERHANG,
|
|
|
|
|
LEF_VIARULE_METALOVERHANG, LEF_VIARULE_VIA,
|
|
|
|
|
LEF_VIARULE_GENERATE, LEF_LAYER_END};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
LefReadLayerSection(
|
|
|
|
|
FILE *f, /* LEF file being read */
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *lname, /* name of the layer */
|
2025-01-04 09:50:40 +01:00
|
|
|
int mode, /* layer, via, or viarule */
|
|
|
|
|
lefLayer *lefl) /* pointer to layer info */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
int keyword, typekey;
|
|
|
|
|
TileType curlayer = -1;
|
|
|
|
|
float fvalue, oscale;
|
|
|
|
|
|
|
|
|
|
/* These are defined in the order of CLASS_* in lefInt.h */
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const layer_type_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"ROUTING",
|
|
|
|
|
"CUT",
|
|
|
|
|
"MASTERSLICE",
|
|
|
|
|
"OVERLAP",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const layer_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"TYPE",
|
|
|
|
|
"WIDTH",
|
2018-02-02 20:16:39 +01:00
|
|
|
"MAXWIDTH",
|
|
|
|
|
"AREA",
|
2017-04-25 14:41:48 +02:00
|
|
|
"SPACING",
|
2018-02-02 20:16:39 +01:00
|
|
|
"SPACINGTABLE",
|
2017-04-25 14:41:48 +02:00
|
|
|
"PITCH",
|
|
|
|
|
"DIRECTION",
|
|
|
|
|
"OFFSET",
|
2018-02-02 20:16:39 +01:00
|
|
|
"WIREEXTENSION",
|
|
|
|
|
"RESISTANCE",
|
|
|
|
|
"CAPACITANCE",
|
|
|
|
|
"EDGECAPACITANCE",
|
|
|
|
|
"THICKNESS",
|
|
|
|
|
"HEIGHT",
|
2019-02-14 18:19:50 +01:00
|
|
|
"PROPERTY",
|
|
|
|
|
"ACCURRENTDENSITY",
|
|
|
|
|
"DCCURRENTDENSITY",
|
2018-02-02 20:16:39 +01:00
|
|
|
"MINIMUMDENSITY",
|
2018-07-19 16:23:30 +02:00
|
|
|
"ANTENNAAREARATIO",
|
2018-02-02 20:16:39 +01:00
|
|
|
"ANTENNADIFFAREARATIO",
|
|
|
|
|
"ANTENNASIDEAREARATIO",
|
2017-04-25 14:41:48 +02:00
|
|
|
"DEFAULT",
|
|
|
|
|
"LAYER",
|
|
|
|
|
"RECT",
|
2019-04-01 17:26:00 +02:00
|
|
|
"FOREIGN",
|
2018-02-02 20:16:39 +01:00
|
|
|
"ENCLOSURE",
|
|
|
|
|
"PREFERENCLOSURE",
|
|
|
|
|
"OVERHANG",
|
|
|
|
|
"METALOVERHANG",
|
2017-04-25 14:41:48 +02:00
|
|
|
"VIA",
|
2018-02-02 20:16:39 +01:00
|
|
|
"GENERATE",
|
2017-04-25 14:41:48 +02:00
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const spacing_keys[] = {
|
2018-02-02 20:16:39 +01:00
|
|
|
"RANGE",
|
|
|
|
|
";",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
oscale = CIFGetOutputScale(1000);
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, layer_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
|
|
|
|
|
token);
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case LEF_LAYER_TYPE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (*token != '\n')
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
typekey = LookupFull(token, layer_type_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (typekey < 0)
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_WARNING, "Unknown layer type \"%s\" in LEF file; "
|
2017-04-25 14:41:48 +02:00
|
|
|
"ignoring.\n", token);
|
|
|
|
|
}
|
|
|
|
|
if (lefl->lefClass != typekey)
|
2018-02-02 20:16:39 +01:00
|
|
|
{
|
|
|
|
|
/* 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)
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Attempt to reclassify layer %s "
|
|
|
|
|
"from %s to %s\n",
|
2018-02-02 20:16:39 +01:00
|
|
|
lname, layer_type_keys[lefl->lefClass],
|
|
|
|
|
layer_type_keys[typekey]);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_LAYER_MAXWIDTH:
|
|
|
|
|
case LEF_LAYER_AREA:
|
|
|
|
|
/* Not handled */
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case LEF_LAYER_SPACING:
|
2018-02-02 20:16:39 +01:00
|
|
|
/* To do: Handle RANGE */
|
2017-04-25 14:41:48 +02:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
sscanf(token, "%f", &fvalue);
|
|
|
|
|
if (lefl->lefClass == CLASS_ROUTE)
|
|
|
|
|
lefl->info.route.spacing = (int)roundf(fvalue / oscale);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2019-02-14 18:19:50 +01:00
|
|
|
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;
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_LAYER_SPACINGTABLE:
|
|
|
|
|
/* To do: Handle spacing tables */
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2025-02-18 14:45:36 +01:00
|
|
|
lefl->info.route.hdirection = (tolower(token[0]) == 'h') ? TRUE : FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_LAYER_OFFSET:
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_LAYER_RES:
|
|
|
|
|
case LEF_LAYER_CAP:
|
|
|
|
|
case LEF_LAYER_EDGECAP:
|
|
|
|
|
case LEF_LAYER_THICKNESS:
|
|
|
|
|
case LEF_LAYER_HEIGHT:
|
|
|
|
|
case LEF_LAYER_MINDENSITY:
|
2018-07-19 16:23:30 +02:00
|
|
|
case LEF_LAYER_ANTENNAAREA:
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_LAYER_ANTENNADIFF:
|
|
|
|
|
case LEF_LAYER_ANTENNASIDE:
|
|
|
|
|
case LEF_LAYER_WIREEXT:
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_VIA_DEFAULT:
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_VIARULE_GENERATE:
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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;
|
2019-04-01 17:26:00 +02:00
|
|
|
case LEF_VIA_FOREIGN:
|
2017-04-25 14:41:48 +02:00
|
|
|
case LEF_VIARULE_VIA:
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_VIA_ENCLOSURE:
|
|
|
|
|
case LEF_VIA_PREFERENCLOSURE:
|
|
|
|
|
case LEF_VIARULE_OVERHANG:
|
|
|
|
|
case LEF_VIARULE_METALOVERHANG:
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_LAYER_END:
|
|
|
|
|
if (LefParseEndStatement(f, lname) == 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Layer END statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
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.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2018-02-02 20:16:39 +01:00
|
|
|
enum lef_sections {LEF_VERSION = 0,
|
|
|
|
|
LEF_BUSBITCHARS, LEF_DIVIDERCHAR, LEF_MANUFACTURINGGRID,
|
|
|
|
|
LEF_USEMINSPACING, LEF_CLEARANCEMEASURE,
|
|
|
|
|
LEF_NAMESCASESENSITIVE,
|
2017-04-25 14:41:48 +02:00
|
|
|
LEF_PROPERTYDEFS, LEF_UNITS, LEF_SECTION_LAYER,
|
2019-04-01 17:26:00 +02:00
|
|
|
LEF_SECTION_VIA, LEF_SECTION_VIARULE, LEF_SECTION_NONDEFAULTRULE,
|
2019-04-01 18:23:48 +02:00
|
|
|
LEF_NOWIREEXTENSIONATPIN, LEF_SECTION_SPACING, LEF_SECTION_SITE,
|
2020-05-29 20:31:48 +02:00
|
|
|
LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP,
|
2017-04-25 14:41:48 +02:00
|
|
|
LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO,
|
|
|
|
|
LEF_END};
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
LefRead(
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *inName,
|
2025-01-04 09:50:40 +01:00
|
|
|
bool importForeign,
|
|
|
|
|
bool doAnnotate,
|
|
|
|
|
int lefTimestamp)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
char *filename;
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
char tsave[128];
|
|
|
|
|
int keyword;
|
|
|
|
|
float oscale;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
lefLayer *lefl;
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const sections[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"VERSION",
|
2018-02-02 20:16:39 +01:00
|
|
|
"BUSBITCHARS",
|
|
|
|
|
"DIVIDERCHAR",
|
|
|
|
|
"MANUFACTURINGGRID",
|
|
|
|
|
"USEMINSPACING",
|
|
|
|
|
"CLEARANCEMEASURE",
|
2017-04-25 14:41:48 +02:00
|
|
|
"NAMESCASESENSITIVE",
|
|
|
|
|
"PROPERTYDEFINITIONS",
|
|
|
|
|
"UNITS",
|
|
|
|
|
"LAYER",
|
|
|
|
|
"VIA",
|
|
|
|
|
"VIARULE",
|
2019-04-01 17:26:00 +02:00
|
|
|
"NONDEFAULTRULE",
|
2019-04-01 18:23:48 +02:00
|
|
|
"NOWIREEXTENSIONATPIN",
|
2017-04-25 14:41:48 +02:00
|
|
|
"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);
|
2019-06-06 15:59:56 +02:00
|
|
|
lefCurrentLine = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, sections);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
|
|
|
|
|
token);
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case LEF_VERSION:
|
2018-02-02 20:16:39 +01:00
|
|
|
case LEF_BUSBITCHARS:
|
|
|
|
|
case LEF_DIVIDERCHAR:
|
|
|
|
|
case LEF_CLEARANCEMEASURE:
|
|
|
|
|
case LEF_MANUFACTURINGGRID:
|
2021-06-29 19:53:00 +02:00
|
|
|
case LEF_USEMINSPACING:
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case LEF_NAMESCASESENSITIVE:
|
2019-04-01 18:23:48 +02:00
|
|
|
case LEF_NOWIREEXTENSIONATPIN:
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
2025-01-04 10:05:56 +01:00
|
|
|
lefl->canonName = (const char *)he->h_key.h_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_WARNING, "Cut type \"%s\" redefined.\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
2025-02-18 14:45:36 +01:00
|
|
|
mtype = LefHelper_DBTechNameType_LefLower(token);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (mtype < 0)
|
|
|
|
|
{
|
2018-02-02 20:16:39 +01:00
|
|
|
/* Ignore. This is probably a masterslice or */
|
|
|
|
|
/* overlap, but that hasn't been parsed yet. */
|
2017-04-25 14:41:48 +02:00
|
|
|
LefSkipSection(f, tsave);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (DBIsContact(mtype) && (keyword == LEF_SECTION_LAYER))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Layer %s maps to a magic contact layer; "
|
2017-04-25 14:41:48 +02:00
|
|
|
"must be defined in lef section of techfile\n",
|
|
|
|
|
token);
|
|
|
|
|
LefSkipSection(f, tsave);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (!DBIsContact(mtype) && (keyword != LEF_SECTION_LAYER))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Via %s maps to a non-contact magic layer; "
|
2017-04-25 14:41:48 +02:00
|
|
|
"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);
|
2025-01-04 10:05:56 +01:00
|
|
|
lefl->canonName = (const char *)he->h_key.h_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
lefl = (lefLayer *)HashGetValue(he);
|
|
|
|
|
if (lefl && lefl->type < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "Layer %s is only defined for "
|
|
|
|
|
"obstructions!\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
LefSkipSection(f, tsave);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LefReadLayerSection(f, tsave, keyword, lefl);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-04-01 17:26:00 +02:00
|
|
|
case LEF_SECTION_NONDEFAULTRULE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
sprintf(tsave, "%.127s", token);
|
2022-03-31 16:23:39 +02:00
|
|
|
LefReadNonDefaultRule(f, tsave, oscale);
|
2019-04-01 17:26:00 +02:00
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2020-06-17 04:54:48 +02:00
|
|
|
case LEF_SECTION_SITE:
|
2017-04-25 14:41:48 +02:00
|
|
|
case LEF_MACRO:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
/* Diagnostic */
|
|
|
|
|
/*
|
|
|
|
|
TxPrintf("LEF file: Defines new cell %s\n", token);
|
|
|
|
|
*/
|
|
|
|
|
sprintf(tsave, "%.127s", token);
|
2022-01-22 19:02:47 +01:00
|
|
|
LefReadMacro(f, tsave, oscale, importForeign, doAnnotate, lefTimestamp);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case LEF_END:
|
|
|
|
|
if (LefParseEndStatement(f, "LIBRARY") == 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_ERROR, "END statement out of context.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == LEF_END) break;
|
|
|
|
|
}
|
|
|
|
|
TxPrintf("LEF read: Processed %d lines.\n", lefCurrentLine);
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(LEF_SUMMARY, NULL); /* print statement of errors, if any */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
|
HashKill(&LefCellTable);
|
|
|
|
|
HashKill(&lefDefInitHash);
|
|
|
|
|
if (f != NULL) fclose(f);
|
|
|
|
|
UndoEnable();
|
|
|
|
|
}
|