2017-04-25 14:41:48 +02:00
|
|
|
/*
|
2020-05-23 00:37:41 +02:00
|
|
|
* lefWrite.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 (May 1, 2003): LEF output for cells, to include pointer to
|
|
|
|
|
* GDS, automatic generation of GDS if not already made, bounding box export,
|
2020-05-23 00:37:41 +02:00
|
|
|
* port export, export of irouter "fence", "magnet", and "rotate" layers
|
2017-04-25 14:41:48 +02:00
|
|
|
* for defining router hints, and generating areas for obstructions and
|
|
|
|
|
* pin layers.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
2020-05-25 21:46:59 +02:00
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header$";
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
2020-06-13 17:05:53 +02:00
|
|
|
#include <math.h> /* for truncf() function */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "database/database.h"
|
2020-05-21 22:26:24 +02:00
|
|
|
#include "extract/extract.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "utils/tech.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "utils/stack.h"
|
|
|
|
|
#include "utils/signals.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "graphics/graphics.h"
|
|
|
|
|
#include "utils/main.h"
|
|
|
|
|
#include "utils/undo.h"
|
2020-04-08 02:08:42 +02:00
|
|
|
#include "drc/drc.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "cif/cif.h"
|
2020-06-13 17:05:53 +02:00
|
|
|
#include "cif/CIFint.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "lef/lefInt.h"
|
|
|
|
|
|
2020-05-23 00:36:38 +02:00
|
|
|
|
2020-06-13 17:05:53 +02:00
|
|
|
#define FP "%s"
|
2020-05-23 00:36:38 +02:00
|
|
|
#define POINT FP " " FP
|
|
|
|
|
#define IN0 " "
|
|
|
|
|
#define IN1 " "
|
|
|
|
|
#define IN2 " "
|
2020-05-23 20:25:42 +02:00
|
|
|
#define IN3 " "
|
2020-05-23 00:36:38 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* ---------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/* Stack of cell definitions */
|
|
|
|
|
Stack *lefDefStack;
|
|
|
|
|
|
2020-06-13 17:05:53 +02:00
|
|
|
/* Keep the database units around for calculations */
|
|
|
|
|
int LEFdbUnits = 1000;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefPrint --
|
|
|
|
|
*
|
|
|
|
|
* Print a measurement value to LEF output in appropriate units. Since
|
|
|
|
|
* the minimum LEF database unit is 1/20 nanometer, the number of digits
|
|
|
|
|
* is adjusted accordingly for the output in units of microns, according
|
|
|
|
|
* to the grid limit (current grid limit in magic is 1 angstrom, so the
|
|
|
|
|
* maximum number of digits behind the decimal is 4).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to a static character string containing the
|
|
|
|
|
* formatted value.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2020-06-15 15:35:21 +02:00
|
|
|
const char *
|
|
|
|
|
lefPrint(char *leffmt, float invalue)
|
2020-06-13 17:05:53 +02:00
|
|
|
{
|
|
|
|
|
float value, r, l;
|
|
|
|
|
|
|
|
|
|
r = (invalue < 0.0) ? -0.5 : 0.5;
|
|
|
|
|
l = (float)LEFdbUnits;
|
|
|
|
|
|
|
|
|
|
/* Truncate to units or half units to the precision indicated by LEFdbUnits */
|
|
|
|
|
|
|
|
|
|
switch (LEFdbUnits)
|
|
|
|
|
{
|
|
|
|
|
case 100:
|
|
|
|
|
value = (float)(truncf((invalue * l) + r) / l);
|
|
|
|
|
sprintf(leffmt, "%.2f", value);
|
|
|
|
|
break;
|
|
|
|
|
case 200:
|
|
|
|
|
case 1000:
|
|
|
|
|
value = (float)(truncf((invalue * l) + r) / l);
|
|
|
|
|
sprintf(leffmt, "%.3f", value);
|
|
|
|
|
break;
|
|
|
|
|
case 2000:
|
|
|
|
|
case 10000:
|
|
|
|
|
value = (float)(truncf((invalue * l) + r) / l);
|
|
|
|
|
sprintf(leffmt, "%.4f", value);
|
|
|
|
|
break;
|
|
|
|
|
case 20000:
|
|
|
|
|
value = (float)(truncf((invalue * l) + r) / l);
|
2020-06-15 15:35:21 +02:00
|
|
|
sprintf(leffmt, "%.5f", value);
|
2020-06-13 17:05:53 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
value = (float)(truncf((invalue * 100000.) + r) / 100000.);
|
|
|
|
|
sprintf(leffmt, "%.5f", value);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return leffmt;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ---------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefFileOpen --
|
|
|
|
|
*
|
|
|
|
|
* Open the .lef file corresponding to a .mag file.
|
|
|
|
|
* If def->cd_file is non-NULL, the .lef file is just def->cd_file with
|
|
|
|
|
* the trailing .mag replaced by .lef. Otherwise, the .lef file is just
|
|
|
|
|
* def->cd_name followed by .lef.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Return a pointer to an open FILE, or NULL if the .lef
|
|
|
|
|
* file could not be opened in the specified mode.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Opens a file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
FILE *
|
|
|
|
|
lefFileOpen(def, file, suffix, mode, prealfile)
|
|
|
|
|
CellDef *def; /* Cell whose .lef file is to be written. Should
|
|
|
|
|
* be NULL if file is being opened for reading.
|
|
|
|
|
*/
|
|
|
|
|
char *file; /* If non-NULL, open 'name'.lef; otherwise,
|
|
|
|
|
* derive filename from 'def' as described
|
|
|
|
|
* above.
|
|
|
|
|
*/
|
|
|
|
|
char *suffix; /* Either ".lef" for LEF files or ".def" for DEF files */
|
|
|
|
|
char *mode; /* Either "r" or "w", the mode in which the LEF/DEF
|
|
|
|
|
* file is to be opened.
|
|
|
|
|
*/
|
|
|
|
|
char **prealfile; /* If this is non-NULL, it gets set to point to
|
|
|
|
|
* a string holding the name of the LEF/DEF file.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
char namebuf[512], *name, *endp, *ends;
|
2019-10-24 22:57:46 +02:00
|
|
|
char *locsuffix;
|
2019-12-16 15:55:11 +01:00
|
|
|
char *pptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
int len;
|
|
|
|
|
FILE *rfile;
|
|
|
|
|
|
2020-05-23 00:37:41 +02:00
|
|
|
if (file)
|
2017-04-25 14:41:48 +02:00
|
|
|
name = file;
|
|
|
|
|
else if (def && def->cd_file)
|
|
|
|
|
name = def->cd_file;
|
|
|
|
|
else if (def)
|
|
|
|
|
name = def->cd_name;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("LEF file open: No file name or cell given\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Strip off suffix, if there is one
|
|
|
|
|
|
|
|
|
|
ends = strrchr(name, '/');
|
|
|
|
|
if (ends == NULL)
|
|
|
|
|
ends = name;
|
|
|
|
|
else
|
|
|
|
|
ends++;
|
|
|
|
|
|
|
|
|
|
if (endp = strrchr(ends, '.'))
|
|
|
|
|
{
|
2019-12-16 15:55:11 +01:00
|
|
|
if (strcmp(endp, suffix))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-07-20 03:56:00 +02:00
|
|
|
/* Try once as-is, with the given extension. That takes care */
|
|
|
|
|
/* of some less-usual extensions like ".tlef". */
|
|
|
|
|
if ((rfile = PaOpen(name, mode, NULL, Path, CellLibPath, prealfile)) != NULL)
|
|
|
|
|
return rfile;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
len = endp - name;
|
|
|
|
|
if (len > sizeof namebuf - 1) len = sizeof namebuf - 1;
|
|
|
|
|
(void) strncpy(namebuf, name, len);
|
|
|
|
|
namebuf[len] = '\0';
|
|
|
|
|
name = namebuf;
|
2019-10-24 22:57:46 +02:00
|
|
|
locsuffix = suffix;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-24 22:57:46 +02:00
|
|
|
else
|
|
|
|
|
locsuffix = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-11-01 18:23:31 +01:00
|
|
|
else
|
|
|
|
|
locsuffix = suffix;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Try once as-is, and if this fails, try stripping any leading */
|
|
|
|
|
/* path information in case cell is in a read-only directory (mode */
|
|
|
|
|
/* "read" only, and if def is non-NULL). */
|
|
|
|
|
|
2019-10-24 22:57:46 +02:00
|
|
|
if ((rfile = PaOpen(name, mode, locsuffix, Path, CellLibPath, prealfile)) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
return rfile;
|
|
|
|
|
|
2020-05-23 00:37:41 +02:00
|
|
|
if (def)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (name == def->cd_name) return NULL;
|
|
|
|
|
name = def->cd_name;
|
|
|
|
|
return (PaOpen(name, mode, suffix, Path, CellLibPath, prealfile));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefWriteHeader --
|
|
|
|
|
*
|
|
|
|
|
* This routine generates LEF header output for a cell or cell hierarchy.
|
|
|
|
|
* Although the LEF/DEF spec does not define a "header" per se, this is
|
|
|
|
|
* considered to be all LEF output not including the MACRO calls. The
|
|
|
|
|
* header, therefore, defines layers, process routing rules, units
|
|
|
|
|
* (lambda), and so forth.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes output to the open file "f".
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2020-06-17 04:54:48 +02:00
|
|
|
lefWriteHeader(def, f, lefTech, propTable, siteTable)
|
2020-05-29 22:12:09 +02:00
|
|
|
CellDef *def; /* Def for which to generate LEF output */
|
|
|
|
|
FILE *f; /* Output to this file */
|
|
|
|
|
bool lefTech; /* If TRUE, write layer information */
|
|
|
|
|
HashTable *propTable; /* Hash table of property definitions */
|
2020-06-17 04:54:48 +02:00
|
|
|
HashTable *siteTable; /* Hash table of sites used */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TileType type;
|
2020-05-29 22:12:09 +02:00
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
int nprops;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
TxPrintf("Diagnostic: Write LEF header for cell %s\n", def->cd_name);
|
|
|
|
|
|
|
|
|
|
/* NOTE: This routine corresponds to Envisia LEF/DEF Language */
|
2020-05-26 23:25:44 +02:00
|
|
|
/* Reference version 5.7 (November 2009). */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-26 23:25:44 +02:00
|
|
|
fprintf(f, "VERSION 5.7 ;\n");
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "NOWIREEXTENSIONATPIN ON ;\n");
|
|
|
|
|
fprintf(f, IN0 "DIVIDERCHAR \"/\" ;\n");
|
|
|
|
|
fprintf(f, IN0 "BUSBITCHARS \"[]\" ;\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-06-13 17:05:53 +02:00
|
|
|
/* Database units are normally 1000 (magic outputs GDS in nanometers) */
|
|
|
|
|
/* but if "gridlimit" is set to something other than 1, then divide it */
|
|
|
|
|
/* down (due to LEF format limitations, grid limit can only be 1, 5, or */
|
|
|
|
|
/* 10). If the CWF_ANGSTROMS flag is set, then multiply up by ten. */
|
|
|
|
|
|
|
|
|
|
LEFdbUnits = 1000;
|
|
|
|
|
if (CIFCurStyle)
|
|
|
|
|
{
|
|
|
|
|
if (CIFCurStyle->cs_flags & CWF_ANGSTROMS) LEFdbUnits *= 10;
|
|
|
|
|
switch (CIFCurStyle->cs_gridLimit)
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
case 5:
|
|
|
|
|
case 10:
|
|
|
|
|
LEFdbUnits /= CIFCurStyle->cs_gridLimit;
|
|
|
|
|
break;
|
|
|
|
|
/* Otherwise leave as-is */
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
fprintf(f, "UNITS\n");
|
2020-06-13 17:05:53 +02:00
|
|
|
fprintf(f, IN0 "DATABASE MICRONS %d ;\n", LEFdbUnits);
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(f, "END UNITS\n");
|
|
|
|
|
fprintf(f, "\n");
|
|
|
|
|
|
2020-05-29 22:12:09 +02:00
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
nprops = 0;
|
|
|
|
|
while (he = HashNext(propTable, &hs))
|
|
|
|
|
{
|
2020-05-29 22:26:37 +02:00
|
|
|
if (nprops == 0) fprintf(f, "PROPERTYDEFINITIONS\n");
|
2020-05-29 22:12:09 +02:00
|
|
|
nprops++;
|
|
|
|
|
|
|
|
|
|
/* NOTE: Type (e.g., "STRING") may be kept in hash value. */
|
|
|
|
|
/* This has not been implemented; only string types are supported */
|
2020-05-29 22:39:40 +02:00
|
|
|
fprintf(f, IN0 "MACRO %s STRING ;\n", (char *)he->h_key.h_name);
|
2020-05-29 22:12:09 +02:00
|
|
|
}
|
2020-06-17 04:54:48 +02:00
|
|
|
if (nprops > 0) fprintf(f, "END PROPERTYDEFINITIONS\n\n");
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while (he = HashNext(siteTable, &hs))
|
|
|
|
|
{
|
|
|
|
|
/* Output the SITE as a macro */
|
|
|
|
|
CellDef *siteDef;
|
|
|
|
|
float scale;
|
|
|
|
|
char leffmt[2][10];
|
|
|
|
|
bool propfound;
|
|
|
|
|
char *propvalue;
|
|
|
|
|
Rect boundary;
|
|
|
|
|
|
|
|
|
|
siteDef = DBCellLookDef((char *)he->h_key.h_name);
|
|
|
|
|
if (siteDef)
|
|
|
|
|
{
|
|
|
|
|
fprintf(f, "SITE %s\n", siteDef->cd_name);
|
|
|
|
|
|
|
|
|
|
propvalue = (char *)DBPropGet(siteDef, "LEFsymmetry", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
fprintf(f, IN0 "SYMMETRY %s ;\n", propvalue);
|
|
|
|
|
else
|
|
|
|
|
/* Usually core cells have symmetry Y only. */
|
|
|
|
|
fprintf(f, IN0 "SYMMETRY Y ;\n");
|
|
|
|
|
|
|
|
|
|
propvalue = (char *)DBPropGet(siteDef, "LEFclass", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
fprintf(f, IN0 "CLASS %s ;\n", propvalue);
|
|
|
|
|
else
|
|
|
|
|
/* Needs a class of some kind. Use CORE as default if not defined */
|
|
|
|
|
fprintf(f, IN0 "CLASS CORE ;\n");
|
|
|
|
|
|
|
|
|
|
boundary = siteDef->cd_bbox;
|
|
|
|
|
if (siteDef->cd_flags & CDFIXEDBBOX)
|
|
|
|
|
{
|
|
|
|
|
propvalue = (char *)DBPropGet(def, "FIXED_BBOX", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
sscanf(propvalue, "%d %d %d %d", &boundary.r_xbot,
|
|
|
|
|
&boundary.r_ybot, &boundary.r_xtop, &boundary.r_ytop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scale = CIFGetOutputScale(1000); /* conversion to microns */
|
|
|
|
|
fprintf(f, IN0 "SIZE " FP " BY " FP " ;\n",
|
|
|
|
|
lefPrint(leffmt[0], scale * (float)(boundary.r_xtop - boundary.r_xbot)),
|
|
|
|
|
lefPrint(leffmt[1], scale * (float)(boundary.r_ytop - boundary.r_ybot)));
|
|
|
|
|
|
|
|
|
|
fprintf(f, "END %s\n\n", siteDef->cd_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-29 22:12:09 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
if (!lefTech) return;
|
|
|
|
|
|
2019-01-02 02:39:16 +01:00
|
|
|
UndoDisable();
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Layers (minimal information) */
|
|
|
|
|
|
|
|
|
|
if (LefInfo.ht_table != (HashEntry **)NULL)
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
lefLayer *lefl;
|
|
|
|
|
|
|
|
|
|
float oscale = CIFGetOutputScale(1000); /* lambda->micron conversion */
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while (he = HashNext(&LefInfo, &hs))
|
|
|
|
|
{
|
|
|
|
|
lefl = (lefLayer *)HashGetValue(he);
|
|
|
|
|
if (!lefl) continue;
|
|
|
|
|
|
|
|
|
|
if (lefl->refCnt > 0)
|
|
|
|
|
{
|
|
|
|
|
/* Avoid writing more than one entry per defined layer */
|
|
|
|
|
if (lefl->refCnt > 1) lefl->refCnt = -lefl->refCnt;
|
|
|
|
|
|
|
|
|
|
/* Ignore obstruction-only layers */
|
2020-05-23 00:37:41 +02:00
|
|
|
if (lefl->type == -1) continue;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Ignore VIA types, report only CUT types here */
|
|
|
|
|
else if ((lefl->lefClass == CLASS_VIA)
|
|
|
|
|
&& lefl->info.via.cell != NULL) continue;
|
|
|
|
|
|
|
|
|
|
/* Ignore boundary types */
|
|
|
|
|
else if (lefl->lefClass == CLASS_BOUND) continue;
|
|
|
|
|
|
|
|
|
|
fprintf(f, "LAYER %s\n", lefl->canonName);
|
|
|
|
|
if (lefl->lefClass == CLASS_VIA)
|
|
|
|
|
{
|
|
|
|
|
int cutarea;
|
|
|
|
|
cutarea = (lefl->info.via.area.r_xtop - lefl->info.via.area.r_xbot);
|
|
|
|
|
cutarea *= (lefl->info.via.area.r_ytop - lefl->info.via.area.r_ybot);
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "TYPE CUT ;\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
if (cutarea > 0)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "CUT AREA %f ;\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
(float)cutarea * oscale * oscale);
|
|
|
|
|
}
|
|
|
|
|
else if (lefl->lefClass == CLASS_ROUTE)
|
|
|
|
|
{
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "TYPE ROUTING ;\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
if (lefl->info.route.pitch > 0)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "PITCH %f ;\n", (float)(lefl->info.route.pitch)
|
2017-04-25 14:41:48 +02:00
|
|
|
* oscale);
|
|
|
|
|
if (lefl->info.route.width > 0)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "WIDTH %f ;\n", (float)(lefl->info.route.width)
|
2017-04-25 14:41:48 +02:00
|
|
|
* oscale);
|
|
|
|
|
if (lefl->info.route.spacing > 0)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "SPACING %f ;\n", (float)(lefl->info.route.spacing)
|
2017-04-25 14:41:48 +02:00
|
|
|
* oscale);
|
|
|
|
|
/* No sense in providing direction info unless we know the width */
|
|
|
|
|
if (lefl->info.route.width > 0)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "DIRECTION %s ;\n", (lefl->info.route.hdirection)
|
2017-04-25 14:41:48 +02:00
|
|
|
? "HORIZONTAL" : "VERTICAL");
|
|
|
|
|
}
|
|
|
|
|
else if (lefl->lefClass == CLASS_MASTER)
|
|
|
|
|
{
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "TYPE MASTERSLICE ;\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else if (lefl->lefClass == CLASS_OVERLAP)
|
|
|
|
|
{
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "TYPE OVERLAP ;\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
fprintf(f, "END %s\n\n", lefl->canonName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return reference counts to normal */
|
|
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while (he = HashNext(&LefInfo, &hs))
|
|
|
|
|
{
|
|
|
|
|
lefl = (lefLayer *)HashGetValue(he);
|
|
|
|
|
if (lefl && lefl->refCnt < 0)
|
|
|
|
|
lefl->refCnt = -lefl->refCnt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Vias (to be completed, presumably) */
|
|
|
|
|
/* Rules (to be completed, presumably) */
|
|
|
|
|
|
|
|
|
|
UndoEnable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define LEF_MODE_PORT 0
|
|
|
|
|
#define LEF_MODE_OBSTRUCT 1
|
2020-05-25 18:26:01 +02:00
|
|
|
#define LEF_MODE_CONTACT 2
|
|
|
|
|
#define LEF_MODE_OBS_CONTACT 3
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
FILE *file; /* file to write to */
|
2017-08-02 04:14:42 +02:00
|
|
|
TileType lastType; /* last type output, so we minimize LAYER
|
2017-04-25 14:41:48 +02:00
|
|
|
* statements.
|
|
|
|
|
*/
|
2017-08-02 04:14:42 +02:00
|
|
|
CellDef *lefFlat; /* Soure CellDef (flattened cell) */
|
2017-04-25 14:41:48 +02:00
|
|
|
CellDef *lefYank; /* CellDef to write into */
|
|
|
|
|
LefMapping *lefMagicMap; /* Layer inverse mapping table */
|
|
|
|
|
TileTypeBitMask rmask; /* mask of routing layer types */
|
|
|
|
|
Point origin; /* origin of cell */
|
|
|
|
|
float oscale; /* units scale conversion factor */
|
2017-08-02 04:14:42 +02:00
|
|
|
int pNum; /* Plane number for tile marking */
|
|
|
|
|
int numWrites; /* Track number of writes to output */
|
2020-05-22 03:53:23 +02:00
|
|
|
bool needHeader; /* TRUE if PIN record header needs to be written */
|
2017-04-25 14:41:48 +02:00
|
|
|
int lefMode; /* can be LEF_MODE_PORT when searching
|
|
|
|
|
* connections into ports, or
|
2020-05-25 18:26:01 +02:00
|
|
|
* LEF_MODE_OBSTRUCT when generating obstruction
|
|
|
|
|
* geometry. LEF polyons must be manhattan, so
|
|
|
|
|
* if we find a split tile, LEF_MODE_PORT ignores
|
|
|
|
|
* it, and LEF_MODE_OBSTRUCT outputs the whole
|
|
|
|
|
* tile. LEF_MODE_CONTACT and LEF_MODE_OBS_CONTACT
|
|
|
|
|
* are equivalent modes for handling contacts.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
} lefClient;
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/*
|
2020-05-25 18:26:01 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefEraseGeometry --
|
|
|
|
|
*
|
|
|
|
|
* Remove geometry that has been output from the lefFlat cell so that it
|
|
|
|
|
* will be ignored on subsequent output. This is how pin areas are
|
|
|
|
|
* separated from obstruction areas in the output: Each pin network is
|
|
|
|
|
* selected and output, then its geometry is erased from lefFlat. The
|
|
|
|
|
* remaining geometry after all pins have been written are the obstructions.
|
2017-08-02 04:14:42 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lefEraseGeometry(tile, cdata)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ClientData cdata;
|
|
|
|
|
{
|
|
|
|
|
lefClient *lefdata = (lefClient *)cdata;
|
|
|
|
|
CellDef *flatDef = lefdata->lefFlat;
|
|
|
|
|
Rect area;
|
2017-10-06 04:08:24 +02:00
|
|
|
TileType ttype, otype;
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
TiToRect(tile, &area);
|
|
|
|
|
|
2017-10-06 04:08:24 +02:00
|
|
|
otype = TiGetTypeExact(tile);
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
ttype = (otype & TT_SIDE) ? SplitRightType(tile) :
|
|
|
|
|
SplitLeftType(tile);
|
|
|
|
|
else
|
|
|
|
|
ttype = otype;
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Erase the tile area out of lefFlat */
|
2020-07-15 23:29:56 +02:00
|
|
|
/* Use DBNMPaintPlane, NOT DBErase(). This erases contacts one */
|
|
|
|
|
/* plane at a time, which normally is bad, but since every plane */
|
|
|
|
|
/* gets erased eventually during "lef write", this is okay. */
|
|
|
|
|
/* DBErase(flatDef, &area, ttype); */
|
|
|
|
|
DBNMPaintPlane(flatDef->cd_planes[lefdata->pNum], otype, &area,
|
|
|
|
|
DBStdEraseTbl(ttype, lefdata->pNum), NULL);
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* Callback function to find the cell boundary based on the specified
|
|
|
|
|
* boundary layer type. Typically this will be a single rectangle on
|
|
|
|
|
* its own plane, but for completeness, all geometry in the cell is
|
|
|
|
|
* checked, and the bounding rectangle adjusted to fit that area.
|
|
|
|
|
*
|
|
|
|
|
* Return 0 to keep the search going.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lefGetBound(tile, cdata)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ClientData cdata;
|
|
|
|
|
{
|
|
|
|
|
Rect *boundary = (Rect *)cdata;
|
|
|
|
|
Rect area;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &area);
|
2020-03-14 18:00:03 +01:00
|
|
|
|
|
|
|
|
if (boundary->r_xtop <= boundary->r_xbot)
|
|
|
|
|
*boundary = area;
|
|
|
|
|
else
|
|
|
|
|
GeoInclude(&area, boundary);
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 22:26:24 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefAccumulateArea --
|
|
|
|
|
*
|
|
|
|
|
* Function called to accumulate the tile area of tiles
|
|
|
|
|
*
|
|
|
|
|
* Return 0 to keep the search going.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lefAccumulateArea(tile, cdata)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ClientData cdata;
|
|
|
|
|
{
|
|
|
|
|
int *area = (int *)cdata;
|
|
|
|
|
Rect rarea;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &rarea);
|
|
|
|
|
|
|
|
|
|
*area += (rarea.r_xtop - rarea.r_xbot) * (rarea.r_ytop - rarea.r_ybot);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-15 23:29:56 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefFindTopmost --
|
|
|
|
|
*
|
|
|
|
|
* Function called to find the topmost layer used by a pin network
|
|
|
|
|
*
|
|
|
|
|
* Return 0 to keep the search going.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lefFindTopmost(tile, cdata)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ClientData cdata;
|
|
|
|
|
{
|
|
|
|
|
return 1; /* Stop processing on the first tile found */
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefYankGeometry --
|
|
|
|
|
*
|
2020-05-25 18:26:01 +02:00
|
|
|
* Function called from lefWriteMacro() that copies geometry from
|
2017-04-25 14:41:48 +02:00
|
|
|
* the cell into a yank buffer cell, one pin connection at a time.
|
2020-05-25 18:26:01 +02:00
|
|
|
* This cell removes contacts so that the following call to lefWriteGeometry
|
|
|
|
|
* will not be cut up into tiles around contacts.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Return 0 to keep the search going.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lefYankGeometry(tile, cdata)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ClientData cdata;
|
|
|
|
|
{
|
|
|
|
|
lefClient *lefdata = (lefClient *)cdata;
|
|
|
|
|
Rect area;
|
|
|
|
|
TileType ttype, otype, ptype;
|
|
|
|
|
LefMapping *lefMagicToLefLayer;
|
2017-08-02 04:14:42 +02:00
|
|
|
TileTypeBitMask sMask;
|
|
|
|
|
bool iscut;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Ignore marked tiles */
|
|
|
|
|
if (tile->ti_client != (ClientData)CLIENTDEFAULT) return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
otype = TiGetTypeExact(tile);
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
ttype = (otype & TT_SIDE) ? SplitRightType(tile) :
|
|
|
|
|
SplitLeftType(tile);
|
|
|
|
|
else
|
|
|
|
|
ttype = otype;
|
|
|
|
|
|
|
|
|
|
/* Output geometry only for defined routing layers */
|
|
|
|
|
/* If we have encountered a contact type, then */
|
|
|
|
|
/* decompose into constituent layers and see if any */
|
|
|
|
|
/* of them are in the route layer masks. */
|
|
|
|
|
|
|
|
|
|
if (DBIsContact(ttype))
|
|
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
DBFullResidueMask(ttype, &sMask);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Use the first routing layer that is represented */
|
|
|
|
|
/* in sMask. If none, then return. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
|
|
|
|
|
if (TTMaskHasType(&sMask, ttype))
|
2017-04-25 14:41:48 +02:00
|
|
|
if (TTMaskHasType(&lefdata->rmask, ttype))
|
2017-08-02 04:14:42 +02:00
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
if (ttype == DBNumTypes) return 0;
|
|
|
|
|
iscut = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&lefdata->rmask, ttype)) return 0;
|
2017-08-02 04:14:42 +02:00
|
|
|
iscut = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &area);
|
|
|
|
|
|
2019-02-06 17:09:46 +01:00
|
|
|
while (ttype < DBNumUserLayers)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
lefMagicToLefLayer = lefdata->lefMagicMap;
|
2020-07-15 23:29:56 +02:00
|
|
|
if (lefMagicToLefLayer[ttype].lefName != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
// Set only the side being yanked
|
|
|
|
|
ptype = (otype & (TT_DIAGONAL | TT_SIDE | TT_DIRECTION)) |
|
|
|
|
|
((otype & TT_SIDE) ? (ttype << 14) : ttype);
|
|
|
|
|
else
|
|
|
|
|
ptype = ttype;
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
/* Paint into yank buffer */
|
|
|
|
|
DBNMPaintPlane(lefdata->lefYank->cd_planes[lefdata->pNum],
|
|
|
|
|
ptype, &area, DBStdPaintTbl(ttype, lefdata->pNum),
|
|
|
|
|
(PaintUndoInfo *)NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
if (iscut == FALSE) break;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
for (++ttype; ttype < DBNumTypes; ttype++)
|
2017-08-02 04:14:42 +02:00
|
|
|
if (TTMaskHasType(&sMask, ttype))
|
|
|
|
|
if (TTMaskHasType(&lefdata->rmask, ttype))
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 18:26:01 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefYankContacts --
|
|
|
|
|
*
|
|
|
|
|
* Function similar to lefYankGeometry (see above) that handles contacts.
|
|
|
|
|
* Contacts are yanked separately so that the tiles around contact cuts
|
|
|
|
|
* are not broken up into pieces, and the contacts can be handled as cuts.
|
|
|
|
|
*
|
|
|
|
|
* Return 0 to keep the search going.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lefYankContacts(tile, cdata)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ClientData cdata;
|
|
|
|
|
{
|
|
|
|
|
lefClient *lefdata = (lefClient *)cdata;
|
|
|
|
|
Rect area;
|
|
|
|
|
TileType ttype, ptype, stype;
|
|
|
|
|
LefMapping *lefMagicToLefLayer;
|
|
|
|
|
TileTypeBitMask sMask, *lrmask;
|
|
|
|
|
bool iscut;
|
|
|
|
|
|
|
|
|
|
/* Ignore marked tiles */
|
|
|
|
|
if (tile->ti_client != (ClientData)CLIENTDEFAULT) return 0;
|
|
|
|
|
|
2020-05-25 22:13:42 +02:00
|
|
|
/* Ignore split tiles */
|
|
|
|
|
if (IsSplit(tile)) return 0;
|
2020-05-25 18:26:01 +02:00
|
|
|
|
|
|
|
|
/* Output geometry only for defined contact layers, on their home plane */
|
2020-05-25 22:13:42 +02:00
|
|
|
ttype = TiGetType(tile);
|
2020-05-25 18:26:01 +02:00
|
|
|
if (!DBIsContact(ttype)) return 0;
|
|
|
|
|
|
|
|
|
|
/* If this is a stacked contact, find any residue contact type with a */
|
|
|
|
|
/* home plane on lefdata->pNum. */
|
|
|
|
|
|
|
|
|
|
if (ttype >= DBNumUserLayers)
|
|
|
|
|
{
|
|
|
|
|
lrmask = DBResidueMask(ttype);
|
|
|
|
|
for (stype = TT_TECHDEPBASE; stype < DBNumUserLayers; stype++)
|
|
|
|
|
if (TTMaskHasType(lrmask, stype))
|
|
|
|
|
if (DBPlane(stype) == lefdata->pNum)
|
|
|
|
|
{
|
|
|
|
|
ttype = stype;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (stype == DBNumUserLayers) return 0; /* Should not happen */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if (DBPlane(ttype) != lefdata->pNum) return 0;
|
|
|
|
|
|
|
|
|
|
/* Ignore non-Manhattan tiles */
|
|
|
|
|
if (IsSplit(tile)) return 0;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &area);
|
|
|
|
|
|
|
|
|
|
lefMagicToLefLayer = lefdata->lefMagicMap;
|
|
|
|
|
if (lefMagicToLefLayer[ttype].lefInfo != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Paint into yank buffer */
|
|
|
|
|
DBNMPaintPlane(lefdata->lefYank->cd_planes[lefdata->pNum],
|
|
|
|
|
ttype, &area, DBStdPaintTbl(ttype, lefdata->pNum),
|
|
|
|
|
(PaintUndoInfo *)NULL);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefWriteGeometry --
|
|
|
|
|
*
|
2020-05-25 18:26:01 +02:00
|
|
|
* Function called from lefWritemacro() that outputs a RECT
|
2017-04-25 14:41:48 +02:00
|
|
|
* record for each tile called. Note that LEF does not define
|
|
|
|
|
* nonmanhattan geometry (see above, comments in lefClient typedef).
|
|
|
|
|
*
|
|
|
|
|
* Return 0 to keep the search going.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lefWriteGeometry(tile, cdata)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ClientData cdata;
|
|
|
|
|
{
|
|
|
|
|
lefClient *lefdata = (lefClient *)cdata;
|
|
|
|
|
FILE *f = lefdata->file;
|
|
|
|
|
float scale = lefdata->oscale;
|
2020-06-15 15:35:21 +02:00
|
|
|
char leffmt[6][10];
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType ttype, otype = TiGetTypeExact(tile);
|
|
|
|
|
LefMapping *lefMagicToLefLayer = lefdata->lefMagicMap;
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Ignore tiles that have already been output */
|
|
|
|
|
if (tile->ti_client != (ClientData)CLIENTDEFAULT)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Mark this tile as visited */
|
|
|
|
|
TiSetClient(tile, (ClientData)1);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Get layer type */
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
ttype = (otype & TT_SIDE) ? SplitRightType(tile) :
|
|
|
|
|
SplitLeftType(tile);
|
|
|
|
|
else
|
|
|
|
|
ttype = otype;
|
|
|
|
|
|
2020-05-25 18:26:01 +02:00
|
|
|
/* Unmarked non-contact tiles are created by tile splitting when */
|
|
|
|
|
/* yanking contacts. These get marked but are otherwise ignored, */
|
|
|
|
|
/* because they are duplicates of areas already output. */
|
|
|
|
|
if (!DBIsContact(ttype))
|
|
|
|
|
if (lefdata->lefMode == LEF_MODE_CONTACT ||
|
|
|
|
|
lefdata->lefMode == LEF_MODE_OBS_CONTACT)
|
|
|
|
|
return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-25 18:26:01 +02:00
|
|
|
/* Only LEF routing layer types will be in the yank buffer */
|
2017-04-25 14:41:48 +02:00
|
|
|
if (!TTMaskHasType(&lefdata->rmask, ttype)) return 0;
|
|
|
|
|
|
2020-05-22 03:53:23 +02:00
|
|
|
if (lefdata->needHeader)
|
|
|
|
|
{
|
|
|
|
|
/* Reset the tile to not visited and return 1 to */
|
|
|
|
|
/* signal that something is going to be written. */
|
|
|
|
|
|
|
|
|
|
TiSetClient(tile, (ClientData)CLIENTDEFAULT);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
if (lefdata->numWrites == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-05-25 18:26:01 +02:00
|
|
|
if (lefdata->lefMode == LEF_MODE_PORT || lefdata->lefMode == LEF_MODE_CONTACT)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN1 "PORT\n");
|
2017-08-02 04:14:42 +02:00
|
|
|
else
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "OBS\n");
|
2017-08-02 04:14:42 +02:00
|
|
|
}
|
|
|
|
|
lefdata->numWrites++;
|
|
|
|
|
|
|
|
|
|
if (ttype != lefdata->lastType)
|
2020-07-15 23:29:56 +02:00
|
|
|
if (lefMagicToLefLayer[ttype].lefName != NULL)
|
2017-08-02 04:14:42 +02:00
|
|
|
{
|
2020-05-23 20:25:42 +02:00
|
|
|
fprintf(f, IN2 "LAYER %s ;\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
lefMagicToLefLayer[ttype].lefName);
|
2017-08-02 04:14:42 +02:00
|
|
|
lefdata->lastType = ttype;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
if (otype & TT_SIDE)
|
|
|
|
|
{
|
|
|
|
|
if (otype & TT_DIRECTION)
|
2020-05-23 20:25:42 +02:00
|
|
|
fprintf(f, IN3 "POLYGON " POINT " " POINT " " POINT " ;\n",
|
2020-06-15 15:35:21 +02:00
|
|
|
lefPrint(leffmt[0], scale * (float)(LEFT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[1], scale * (float)(TOP(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[2], scale * (float)(RIGHT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[3], scale * (float)(TOP(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[4], scale * (float)(RIGHT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[5], scale * (float)(BOTTOM(tile)
|
|
|
|
|
- lefdata->origin.p_y)));
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2020-05-23 20:25:42 +02:00
|
|
|
fprintf(f, IN3 "POLYGON " POINT " " POINT " " POINT " ;\n",
|
2020-06-15 15:35:21 +02:00
|
|
|
lefPrint(leffmt[0], scale * (float)(RIGHT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[1], scale * (float)(TOP(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[2], scale * (float)(RIGHT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[3], scale * (float)(BOTTOM(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[4], scale * (float)(LEFT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[5], scale * (float)(BOTTOM(tile)
|
|
|
|
|
- lefdata->origin.p_y)));
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (otype & TT_DIRECTION)
|
2020-05-23 20:25:42 +02:00
|
|
|
fprintf(f, IN3 "POLYGON " POINT " " POINT " " POINT " ;\n",
|
2020-06-15 15:35:21 +02:00
|
|
|
lefPrint(leffmt[0], scale * (float)(LEFT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[1], scale * (float)(TOP(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[2], scale * (float)(RIGHT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[3], scale * (float)(BOTTOM(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[4], scale * (float)(LEFT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[5], scale * (float)(BOTTOM(tile)
|
|
|
|
|
- lefdata->origin.p_y)));
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2020-05-23 20:25:42 +02:00
|
|
|
fprintf(f, IN3 "POLYGON " POINT " " POINT " " POINT " ;\n",
|
2020-06-15 15:35:21 +02:00
|
|
|
lefPrint(leffmt[0], scale * (float)(LEFT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[1], scale * (float)(TOP(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[2], scale * (float)(RIGHT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[3], scale * (float)(TOP(tile)
|
|
|
|
|
- lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[4], scale * (float)(LEFT(tile)
|
|
|
|
|
- lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[5], scale * (float)(BOTTOM(tile)
|
|
|
|
|
- lefdata->origin.p_y)));
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
2020-05-23 20:25:42 +02:00
|
|
|
fprintf(f, IN3 "RECT " POINT " " POINT " ;\n",
|
2020-06-15 15:35:21 +02:00
|
|
|
lefPrint(leffmt[0], scale * (float)(LEFT(tile) - lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[1], scale * (float)(BOTTOM(tile) - lefdata->origin.p_y)),
|
|
|
|
|
lefPrint(leffmt[2], scale * (float)(RIGHT(tile) - lefdata->origin.p_x)),
|
|
|
|
|
lefPrint(leffmt[3], scale * (float)(TOP(tile) - lefdata->origin.p_y)));
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* MakeLegalLEFSyntax --
|
|
|
|
|
*
|
|
|
|
|
* Follow syntactical rules of the LEF spec. Most notably, Magic
|
|
|
|
|
* node names often contain the hash mark '#', which is illegal
|
|
|
|
|
* in LEF output. Other illegal LEF characters are space, newline,
|
|
|
|
|
* semicolon, and for literal names: dash, asterisk, and percent.
|
|
|
|
|
* All of the above will be replaced with underscores if found.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns an allocated string containing the modified result, or
|
|
|
|
|
* else returns the original string pointer. It is the responsibility
|
|
|
|
|
* of the calling function to free the result if it is not equal to
|
|
|
|
|
* the argument.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Allocated memory.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
MakeLegalLEFSyntax(text)
|
|
|
|
|
char *text;
|
|
|
|
|
{
|
|
|
|
|
static char *badLEFchars = ";# -*$\n";
|
|
|
|
|
char *cptr, *bptr;
|
|
|
|
|
char *rstr;
|
|
|
|
|
|
|
|
|
|
for (cptr = text; *cptr != '\0'; cptr++)
|
|
|
|
|
for (bptr = badLEFchars; *bptr != '\0'; bptr++)
|
|
|
|
|
if (*cptr == *bptr) break;
|
|
|
|
|
|
|
|
|
|
if (*cptr == '\0' && *bptr == '\0')
|
|
|
|
|
return text;
|
|
|
|
|
|
2020-05-23 00:37:41 +02:00
|
|
|
rstr = StrDup((char **)NULL, text);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (cptr = rstr; *cptr != '\0'; cptr++)
|
|
|
|
|
for (bptr = badLEFchars; bptr != '\0'; bptr++)
|
2020-05-23 00:37:41 +02:00
|
|
|
if (*cptr == *bptr)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
*cptr = '_';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rstr;
|
|
|
|
|
}
|
2020-05-23 00:37:41 +02:00
|
|
|
|
2020-03-21 02:29:29 +01:00
|
|
|
/* Linked list structure for holding PIN PORT geometry areas */
|
|
|
|
|
|
|
|
|
|
typedef struct _labelLinkedList {
|
|
|
|
|
Label *lll_label;
|
|
|
|
|
Rect lll_area;
|
|
|
|
|
struct _labelLinkedList *lll_next;
|
|
|
|
|
} labelLinkedList;
|
|
|
|
|
|
2020-05-22 03:53:23 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* LefWritePinHeader --
|
|
|
|
|
*
|
|
|
|
|
* Write the PIN record for the LEF macro along with any known properties
|
|
|
|
|
* such as CLASS and USE. Discover the USE POWER or GROUND if it is not
|
|
|
|
|
* set as a property and the label name matches the Tcl variables $VDD
|
|
|
|
|
* or $GND.
|
|
|
|
|
*
|
|
|
|
|
* Returns TRUE if the pin is a power pin, otherwise FALSE.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
LefWritePinHeader(f, lab)
|
|
|
|
|
FILE *f;
|
|
|
|
|
Label *lab;
|
|
|
|
|
{
|
|
|
|
|
bool ispwrrail = FALSE;
|
|
|
|
|
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "PIN %s\n", lab->lab_text);
|
2020-05-22 03:53:23 +02:00
|
|
|
if (lab->lab_flags & PORT_CLASS_MASK)
|
|
|
|
|
{
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN1 "DIRECTION ");
|
2020-05-22 03:53:23 +02:00
|
|
|
switch(lab->lab_flags & PORT_CLASS_MASK)
|
|
|
|
|
{
|
|
|
|
|
case PORT_CLASS_INPUT:
|
|
|
|
|
fprintf(f, "INPUT");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_CLASS_OUTPUT:
|
|
|
|
|
fprintf(f, "OUTPUT");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_CLASS_TRISTATE:
|
|
|
|
|
fprintf(f, "OUTPUT TRISTATE");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_CLASS_BIDIRECTIONAL:
|
|
|
|
|
fprintf(f, "INOUT");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_CLASS_FEEDTHROUGH:
|
|
|
|
|
fprintf(f, "FEEDTHRU");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fprintf(f, " ;\n");
|
|
|
|
|
}
|
|
|
|
|
ispwrrail = FALSE;
|
|
|
|
|
if (lab->lab_flags & PORT_USE_MASK)
|
|
|
|
|
{
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN1 "USE ");
|
2020-05-22 03:53:23 +02:00
|
|
|
switch(lab->lab_flags & PORT_USE_MASK)
|
|
|
|
|
{
|
|
|
|
|
case PORT_USE_SIGNAL:
|
|
|
|
|
fprintf(f, "SIGNAL");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_USE_ANALOG:
|
|
|
|
|
fprintf(f, "ANALOG");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_USE_POWER:
|
|
|
|
|
fprintf(f, "POWER");
|
|
|
|
|
ispwrrail = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case PORT_USE_GROUND:
|
|
|
|
|
fprintf(f, "GROUND");
|
|
|
|
|
ispwrrail = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case PORT_USE_CLOCK:
|
|
|
|
|
fprintf(f, "CLOCK");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fprintf(f, " ;\n");
|
|
|
|
|
}
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *pwr;
|
|
|
|
|
|
|
|
|
|
/* Determine power rails by matching the $VDD and $GND Tcl variables */
|
|
|
|
|
|
|
|
|
|
pwr = (char *)Tcl_GetVar(magicinterp, "VDD", TCL_GLOBAL_ONLY);
|
|
|
|
|
if (pwr && (!strcmp(lab->lab_text, pwr)))
|
|
|
|
|
{
|
|
|
|
|
ispwrrail = TRUE;
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN1 "USE POWER ;\n");
|
2020-05-22 03:53:23 +02:00
|
|
|
}
|
|
|
|
|
pwr = (char *)Tcl_GetVar(magicinterp, "GND", TCL_GLOBAL_ONLY);
|
|
|
|
|
if (pwr && (!strcmp(lab->lab_text, pwr)))
|
|
|
|
|
{
|
|
|
|
|
ispwrrail = TRUE;
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN1 "USE GROUND ;\n");
|
2020-05-22 03:53:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2020-05-29 15:35:54 +02:00
|
|
|
if (lab->lab_flags & PORT_SHAPE_MASK)
|
|
|
|
|
{
|
|
|
|
|
fprintf(f, IN1 "SHAPE ");
|
|
|
|
|
switch(lab->lab_flags & PORT_SHAPE_MASK)
|
|
|
|
|
{
|
|
|
|
|
case PORT_SHAPE_ABUT:
|
|
|
|
|
fprintf(f, "ABUTMENT");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_SHAPE_RING:
|
|
|
|
|
fprintf(f, "RING");
|
|
|
|
|
break;
|
|
|
|
|
case PORT_SHAPE_THRU:
|
|
|
|
|
fprintf(f, "FEEDTHRU");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fprintf(f, " ;\n");
|
|
|
|
|
}
|
2020-05-22 03:53:23 +02:00
|
|
|
return ispwrrail;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefWriteMacro --
|
|
|
|
|
*
|
|
|
|
|
* This routine generates LEF output for a cell in the form of a LEF
|
|
|
|
|
* "MACRO" block. Includes information on cell dimensions, pins,
|
|
|
|
|
* ports (physical layout associated with pins), and routing obstructions.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes output to the open file "f".
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2020-07-15 23:29:56 +02:00
|
|
|
lefWriteMacro(def, f, scale, hide, toplayer)
|
2017-04-25 14:41:48 +02:00
|
|
|
CellDef *def; /* Def for which to generate LEF output */
|
|
|
|
|
FILE *f; /* Output to this file */
|
|
|
|
|
float scale; /* Output distance units conversion factor */
|
2018-11-19 21:01:20 +01:00
|
|
|
bool hide; /* If TRUE, hide all detail except pins */
|
2020-07-15 23:29:56 +02:00
|
|
|
bool toplayer; /* If TRUE, only output topmost layer of pins */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-05-21 22:26:24 +02:00
|
|
|
bool propfound, ispwrrail;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *propvalue, *class = NULL;
|
2020-03-14 18:00:03 +01:00
|
|
|
Label *lab, *tlab, *reflab;
|
2017-08-02 04:14:42 +02:00
|
|
|
Rect boundary, labr;
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
CellDef *lefFlatDef;
|
|
|
|
|
CellUse lefFlatUse, lefSourceUse;
|
2020-05-21 22:26:24 +02:00
|
|
|
TileTypeBitMask lmask, boundmask, *lrmask, gatetypemask, difftypemask;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType ttype;
|
|
|
|
|
lefClient lc;
|
2020-07-15 23:29:56 +02:00
|
|
|
int idx, pNum, pTop, maxport, curport;
|
2020-06-15 15:35:21 +02:00
|
|
|
char leffmt[2][10];
|
2017-04-25 14:41:48 +02:00
|
|
|
char *LEFtext;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
2020-03-21 02:29:29 +01:00
|
|
|
labelLinkedList *lll = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
extern CellDef *SelectDef;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
UndoDisable();
|
|
|
|
|
|
|
|
|
|
TxPrintf("Diagnostic: Writing LEF output for cell %s\n", def->cd_name);
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
lefFlatDef = DBCellLookDef("__lefFlat__");
|
|
|
|
|
if (lefFlatDef == (CellDef *)NULL)
|
2020-03-21 17:40:35 +01:00
|
|
|
lefFlatDef = DBCellNewDef("__lefFlat__");
|
2017-08-02 04:14:42 +02:00
|
|
|
DBCellSetAvail(lefFlatDef);
|
|
|
|
|
lefFlatDef->cd_flags |= CDINTERNAL;
|
|
|
|
|
|
|
|
|
|
lefFlatUse.cu_id = StrDup((char **)NULL, "Flattened cell");
|
|
|
|
|
lefFlatUse.cu_expandMask = CU_DESCEND_SPECIAL;
|
|
|
|
|
lefFlatUse.cu_def = lefFlatDef;
|
|
|
|
|
DBSetTrans(&lefFlatUse, &GeoIdentityTransform);
|
2020-05-23 00:37:41 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
lefSourceUse.cu_id = StrDup((char **)NULL, "Source cell");
|
|
|
|
|
lefSourceUse.cu_expandMask = CU_DESCEND_ALL;
|
|
|
|
|
lefSourceUse.cu_def = def;
|
|
|
|
|
DBSetTrans(&lefSourceUse, &GeoIdentityTransform);
|
|
|
|
|
|
|
|
|
|
scx.scx_use = &lefSourceUse;
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
scx.scx_area = def->cd_bbox;
|
|
|
|
|
DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_ALL, &lefFlatUse);
|
|
|
|
|
|
|
|
|
|
/* Reset scx to point to the flattened use */
|
|
|
|
|
scx.scx_use = &lefFlatUse;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Set up client record. */
|
|
|
|
|
|
|
|
|
|
lc.file = f;
|
|
|
|
|
lc.oscale = scale;
|
|
|
|
|
lc.lefMagicMap = defMakeInverseLayerMap();
|
2017-08-02 04:14:42 +02:00
|
|
|
lc.lastType = TT_SPACE;
|
|
|
|
|
lc.lefFlat = lefFlatDef;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
TxPrintf("Diagnostic: Scale value is %f\n", lc.oscale);
|
|
|
|
|
|
|
|
|
|
/* Which layers are routing layers are defined in the tech file. */
|
|
|
|
|
|
|
|
|
|
TTMaskZero(&lc.rmask);
|
|
|
|
|
TTMaskZero(&boundmask);
|
2018-11-19 21:01:20 +01:00
|
|
|
TTMaskZero(&lmask);
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
/* Any layer which has a port label attached to it should by */
|
|
|
|
|
/* necessity be considered a routing layer. Usually this will not */
|
|
|
|
|
/* add anything to the mask already created. */
|
|
|
|
|
|
|
|
|
|
for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
TTMaskSetType(&lc.rmask, lab->lab_type);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while (he = HashNext(&LefInfo, &hs))
|
|
|
|
|
{
|
|
|
|
|
lefLayer *lefl = (lefLayer *)HashGetValue(he);
|
|
|
|
|
if (lefl && (lefl->lefClass == CLASS_ROUTE || lefl->lefClass == CLASS_VIA))
|
|
|
|
|
if (lefl->type != -1)
|
|
|
|
|
{
|
|
|
|
|
TTMaskSetType(&lc.rmask, lefl->type);
|
|
|
|
|
if (DBIsContact(lefl->type))
|
|
|
|
|
{
|
|
|
|
|
lrmask = DBResidueMask(lefl->type);
|
|
|
|
|
TTMaskSetMask(&lc.rmask, lrmask);
|
|
|
|
|
}
|
2018-11-19 21:01:20 +01:00
|
|
|
if ((lefl->lefClass == CLASS_ROUTE) && (lefl->obsType != -1))
|
|
|
|
|
TTMaskSetType(&lmask, lefl->type);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
if (lefl->obsType != -1)
|
|
|
|
|
TTMaskSetType(&lc.rmask, lefl->obsType);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (lefl && (lefl->lefClass == CLASS_BOUND))
|
|
|
|
|
if (lefl->type != -1)
|
|
|
|
|
TTMaskSetType(&boundmask, lefl->type);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 22:26:24 +02:00
|
|
|
/* Gate and diff types are determined from the extraction style */
|
|
|
|
|
ExtGetGateTypesMask(&gatetypemask);
|
|
|
|
|
ExtGetDiffTypesMask(&difftypemask);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* NOTE: This routine corresponds to Envisia LEF/DEF Language */
|
|
|
|
|
/* Reference version 5.3 (May 31, 2000) */
|
|
|
|
|
|
|
|
|
|
/* Macro header information (to be completed) */
|
|
|
|
|
|
2020-05-23 00:37:41 +02:00
|
|
|
fprintf(f, "MACRO %s\n", def->cd_name);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* LEF data is stored in the "cd_props" hash table. If the hash */
|
|
|
|
|
/* table is NULL or a specific property undefined, then the LEF */
|
|
|
|
|
/* value takes the default. Generally, LEF properties which have */
|
|
|
|
|
/* default values are optional, so in this case we will leave those */
|
|
|
|
|
/* entries blank. */
|
|
|
|
|
|
|
|
|
|
propvalue = (char *)DBPropGet(def, "LEFclass", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
{
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "CLASS %s ;\n", propvalue);
|
2017-04-25 14:41:48 +02:00
|
|
|
class = propvalue;
|
|
|
|
|
}
|
2018-12-13 18:04:10 +01:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Needs a class of some kind. Use BLOCK as default if not defined */
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "CLASS BLOCK ;\n");
|
2018-12-13 18:04:10 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "FOREIGN %s ;\n", def->cd_name);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* If a boundary class was declared in the LEF section, then use */
|
|
|
|
|
/* that layer type to define the boundary. Otherwise, the cell */
|
|
|
|
|
/* boundary is defined by the magic database. If the boundary */
|
|
|
|
|
/* class is used, and the boundary layer corner is not on the */
|
|
|
|
|
/* origin, then shift all geometry by the difference. */
|
|
|
|
|
|
|
|
|
|
if (!TTMaskIsZero(&boundmask))
|
|
|
|
|
{
|
2020-03-14 18:00:03 +01:00
|
|
|
boundary.r_xbot = boundary.r_xtop = 0;
|
2017-10-06 18:38:48 +02:00
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatUse.cu_def->cd_planes[pNum],
|
2017-04-25 14:41:48 +02:00
|
|
|
&TiPlaneRect, &boundmask, lefGetBound,
|
|
|
|
|
(ClientData)(&boundary));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
boundary = def->cd_bbox;
|
|
|
|
|
|
2019-11-13 21:10:01 +01:00
|
|
|
/* If a bounding box has been declared with the FIXED_BBOX property */
|
|
|
|
|
/* then it takes precedence over def->cd_bbox. */
|
|
|
|
|
|
|
|
|
|
if (def->cd_flags & CDFIXEDBBOX)
|
|
|
|
|
{
|
|
|
|
|
char *propvalue;
|
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
|
|
propvalue = (char *)DBPropGet(def, "FIXED_BBOX", &found);
|
|
|
|
|
if (found)
|
|
|
|
|
sscanf(propvalue, "%d %d %d %d", &boundary.r_xbot,
|
|
|
|
|
&boundary.r_ybot, &boundary.r_xtop, &boundary.r_ytop);
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Write position and size information */
|
2020-05-26 23:19:57 +02:00
|
|
|
/* Note: Using "0.0 - X" prevents fprintf from generating "negative */
|
|
|
|
|
/* zeros" in the output. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "ORIGIN " POINT " ;\n",
|
2020-06-15 15:35:21 +02:00
|
|
|
lefPrint(leffmt[0], 0.0 - lc.oscale * (float)boundary.r_xbot),
|
|
|
|
|
lefPrint(leffmt[1], 0.0 - lc.oscale * (float)boundary.r_ybot));
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "SIZE " FP " BY " FP " ;\n",
|
2020-06-15 15:35:21 +02:00
|
|
|
lefPrint(leffmt[0], lc.oscale * (float)(boundary.r_xtop
|
|
|
|
|
- boundary.r_xbot)),
|
|
|
|
|
lefPrint(leffmt[1], lc.oscale * (float)(boundary.r_ytop
|
|
|
|
|
- boundary.r_ybot)));
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
lc.origin.p_x = 0;
|
|
|
|
|
lc.origin.p_y = 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
propvalue = (char *)DBPropGet(def, "LEFsymmetry", &propfound);
|
|
|
|
|
if (propfound)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "SYMMETRY %s ;\n", propvalue);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-04-01 17:01:13 +02:00
|
|
|
propvalue = (char *)DBPropGet(def, "LEFsite", &propfound);
|
|
|
|
|
if (propfound)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "SITE %s ;\n", propvalue);
|
2020-04-01 17:01:13 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Generate cell for yanking obstructions */
|
|
|
|
|
|
|
|
|
|
lc.lefYank = DBCellLookDef("__lefYank__");
|
|
|
|
|
if (lc.lefYank == (CellDef *)NULL)
|
2020-03-21 17:40:35 +01:00
|
|
|
lc.lefYank = DBCellNewDef("__lefYank__");
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
DBCellSetAvail(lc.lefYank);
|
|
|
|
|
lc.lefYank->cd_flags |= CDINTERNAL;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* List of pins (ports) (to be refined?) */
|
|
|
|
|
|
|
|
|
|
lc.lefMode = LEF_MODE_PORT;
|
2020-03-25 20:21:05 +01:00
|
|
|
lc.numWrites = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Determine the maximum port number, then output ports in order */
|
|
|
|
|
maxport = -1;
|
|
|
|
|
curport = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
curport++;
|
2017-04-25 14:41:48 +02:00
|
|
|
idx = lab->lab_flags & PORT_NUM_MASK;
|
2017-08-02 04:14:42 +02:00
|
|
|
if (idx > maxport)
|
|
|
|
|
maxport = idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maxport < 0) lab = def->cd_labels;
|
|
|
|
|
|
|
|
|
|
/* Work through pins in port order, if defined, otherwise */
|
|
|
|
|
/* in order of the label list. */
|
|
|
|
|
|
|
|
|
|
for (idx = 0; idx < ((maxport < 0) ? curport : maxport + 1); idx++)
|
|
|
|
|
{
|
|
|
|
|
if (maxport >= 0)
|
|
|
|
|
{
|
|
|
|
|
for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
if (!(lab->lab_flags & PORT_VISITED))
|
|
|
|
|
if ((lab->lab_flags & PORT_NUM_MASK) == idx)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
while (lab && !(lab->lab_flags & PORT_DIR_MASK)) lab = lab->lab_next;
|
|
|
|
|
|
|
|
|
|
if (lab == NULL) continue; /* Happens if indexes are skipped */
|
|
|
|
|
|
|
|
|
|
/* Ignore ports which we have already visited (shouldn't happen */
|
|
|
|
|
/* unless ports are shorted together). */
|
|
|
|
|
|
|
|
|
|
if (lab->lab_flags & PORT_VISITED) continue;
|
|
|
|
|
|
|
|
|
|
/* Query pin geometry for SHAPE (to be done?) */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Generate port layout geometry using SimSrConnect() */
|
2020-05-25 18:26:01 +02:00
|
|
|
/* (through the call to SelectNet()) */
|
|
|
|
|
/* */
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Selects all electrically-connected material into the */
|
|
|
|
|
/* select def. Output all the layers and geometries of */
|
|
|
|
|
/* the select def. */
|
|
|
|
|
/* */
|
|
|
|
|
/* We use SimSrConnect() and not DBSrConnect() because */
|
|
|
|
|
/* SimSrConnect() leaves "marks" (tile->ti_client = 1) */
|
|
|
|
|
/* which allows us to later search through all tiles for */
|
|
|
|
|
/* anything that is not connected to a port, and generate */
|
|
|
|
|
/* an "obstruction" record for it. */
|
|
|
|
|
/* */
|
|
|
|
|
/* Note: Use DBIsContact() to check if the layer is a VIA. */
|
|
|
|
|
/* Presently, I am treating contacts like any other layer. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-22 03:53:23 +02:00
|
|
|
lc.needHeader = TRUE;
|
2020-03-14 18:00:03 +01:00
|
|
|
reflab = lab;
|
2017-08-02 04:14:42 +02:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
while (lab != NULL)
|
2017-08-02 04:14:42 +02:00
|
|
|
{
|
2020-05-22 03:53:23 +02:00
|
|
|
int antgatearea, antdiffarea;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
labr = lab->lab_rect;
|
2018-02-21 16:02:23 +01:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
/* Deal with degenerate (line or point) labels */
|
|
|
|
|
/* by growing by 1 in each direction. */
|
2018-11-19 21:01:20 +01:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
if (labr.r_xtop - labr.r_xbot == 0)
|
|
|
|
|
{
|
|
|
|
|
labr.r_xtop++;
|
|
|
|
|
labr.r_xbot--;
|
|
|
|
|
}
|
|
|
|
|
if (labr.r_ytop - labr.r_ybot == 0)
|
|
|
|
|
{
|
|
|
|
|
labr.r_ytop++;
|
|
|
|
|
labr.r_ybot--;
|
|
|
|
|
}
|
2019-11-13 03:46:52 +01:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
// Avoid errors caused by labels attached to space or
|
|
|
|
|
// various technology file issues.
|
|
|
|
|
TTMaskClearType(&lc.rmask, TT_SPACE);
|
2019-11-13 03:46:52 +01:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
scx.scx_area = labr;
|
|
|
|
|
SelectClear();
|
2017-08-02 04:14:42 +02:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
if (hide)
|
|
|
|
|
{
|
2020-03-21 02:29:29 +01:00
|
|
|
Rect carea;
|
|
|
|
|
labelLinkedList *newlll;
|
|
|
|
|
|
|
|
|
|
SelectChunk(&scx, lab->lab_type, 0, &carea, FALSE);
|
2020-04-01 14:59:19 +02:00
|
|
|
if (GEO_RECTNULL(&carea)) carea = labr;
|
2017-08-02 04:14:42 +02:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
/* Note that a sticky label could be placed over multiple */
|
|
|
|
|
/* tile types, which would cause SelectChunk to fail. So */
|
|
|
|
|
/* always paint the label type into the label area in */
|
|
|
|
|
/* SelectDef. */
|
|
|
|
|
|
|
|
|
|
pNum = DBPlane(lab->lab_type);
|
2020-03-21 02:29:29 +01:00
|
|
|
DBPaintPlane(SelectDef->cd_planes[pNum], &carea,
|
2020-03-14 18:00:03 +01:00
|
|
|
DBStdPaintTbl(lab->lab_type, pNum), (PaintUndoInfo *) NULL);
|
2020-03-21 02:29:29 +01:00
|
|
|
|
|
|
|
|
/* Remember this area since it's going to get erased */
|
|
|
|
|
newlll = (labelLinkedList *)mallocMagic(sizeof(labelLinkedList));
|
|
|
|
|
newlll->lll_label = lab;
|
|
|
|
|
newlll->lll_area = carea;
|
|
|
|
|
newlll->lll_next = lll;
|
|
|
|
|
lll = newlll;
|
2020-03-14 18:00:03 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
SelectNet(&scx, lab->lab_type, 0, NULL, FALSE);
|
|
|
|
|
|
2020-05-21 22:26:24 +02:00
|
|
|
// Search for gate and diff types and accumulate antenna
|
|
|
|
|
// areas. For gates, check for all gate types tied to
|
|
|
|
|
// devices with MOSFET types (including "msubcircuit", etc.).
|
|
|
|
|
// For diffusion, use the types declared in the "tiedown"
|
|
|
|
|
// statement in the extract section of the techfile.
|
|
|
|
|
|
2020-05-22 03:53:23 +02:00
|
|
|
antgatearea = 0;
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
2020-05-21 22:26:24 +02:00
|
|
|
{
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
2020-05-21 22:26:24 +02:00
|
|
|
&TiPlaneRect, &gatetypemask,
|
2020-05-22 03:53:23 +02:00
|
|
|
lefAccumulateArea, (ClientData) &antgatearea);
|
2020-05-23 02:13:33 +02:00
|
|
|
// Stop after first plane with geometry to avoid double-counting
|
|
|
|
|
// contacts.
|
|
|
|
|
if (antgatearea > 0) break;
|
2020-05-22 03:53:23 +02:00
|
|
|
}
|
2020-05-21 22:26:24 +02:00
|
|
|
|
2020-05-22 03:53:23 +02:00
|
|
|
antdiffarea = 0;
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
2020-05-21 22:26:24 +02:00
|
|
|
&TiPlaneRect, &difftypemask,
|
2020-05-22 03:53:23 +02:00
|
|
|
lefAccumulateArea, (ClientData) &antdiffarea);
|
2020-05-23 02:13:33 +02:00
|
|
|
// Stop after first plane with geometry to avoid double-counting
|
|
|
|
|
// contacts.
|
|
|
|
|
if (antdiffarea > 0) break;
|
2020-05-21 22:26:24 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-15 23:29:56 +02:00
|
|
|
if (toplayer)
|
|
|
|
|
{
|
|
|
|
|
for (pTop = DBNumPlanes - 1; pTop >= PL_TECHDEPBASE; pTop--)
|
|
|
|
|
{
|
|
|
|
|
if (DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pTop],
|
|
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefFindTopmost, (ClientData)NULL) == 1)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
// For all geometry in the selection, write LEF records,
|
|
|
|
|
// and mark the corresponding tiles in lefFlatDef as
|
|
|
|
|
// visited.
|
|
|
|
|
|
|
|
|
|
lc.numWrites = 0;
|
|
|
|
|
lc.lastType = TT_SPACE;
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
2020-07-15 23:29:56 +02:00
|
|
|
/* Option to output only the topmost layer of a network */
|
|
|
|
|
/* as PIN geometry. All layers below it are considered */
|
|
|
|
|
/* obstructions. */
|
|
|
|
|
if (toplayer) pNum = pTop;
|
|
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
lc.pNum = pNum;
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
2017-08-02 04:14:42 +02:00
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankGeometry, (ClientData) &lc);
|
|
|
|
|
|
2020-05-23 00:37:41 +02:00
|
|
|
while (DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
2017-08-02 04:14:42 +02:00
|
|
|
&TiPlaneRect, &lc.rmask,
|
2020-05-22 03:53:23 +02:00
|
|
|
lefWriteGeometry, (ClientData) &lc) == 1)
|
|
|
|
|
{
|
|
|
|
|
/* needHeader was set and there was something to write, */
|
2020-05-29 17:55:30 +02:00
|
|
|
/* so write the header and then re-run the search. */
|
2020-05-22 03:53:23 +02:00
|
|
|
|
|
|
|
|
ispwrrail = LefWritePinHeader(f, lab);
|
|
|
|
|
if (ispwrrail == FALSE)
|
|
|
|
|
{
|
|
|
|
|
if (antgatearea > 0)
|
2020-06-13 17:15:33 +02:00
|
|
|
fprintf(f, IN1 "ANTENNAGATEAREA %f ;\n",
|
|
|
|
|
lc.oscale * lc.oscale * (float)antgatearea);
|
2020-05-22 03:53:23 +02:00
|
|
|
if (antdiffarea > 0)
|
2020-06-13 17:15:33 +02:00
|
|
|
fprintf(f, IN1 "ANTENNADIFFAREA %f ;\n",
|
|
|
|
|
lc.oscale * lc.oscale * (float)antdiffarea);
|
2020-05-22 03:53:23 +02:00
|
|
|
}
|
|
|
|
|
lc.needHeader = FALSE;
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
2017-08-02 04:14:42 +02:00
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefEraseGeometry, (ClientData) &lc);
|
2020-05-25 18:26:01 +02:00
|
|
|
|
|
|
|
|
/* Second round yank & write, for contacts only */
|
|
|
|
|
|
|
|
|
|
lc.lefMode = LEF_MODE_CONTACT;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
|
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankContacts, (ClientData) &lc);
|
|
|
|
|
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
|
|
|
|
&TiPlaneRect, &lc.rmask,
|
|
|
|
|
lefWriteGeometry, (ClientData) &lc);
|
|
|
|
|
lc.lefMode = LEF_MODE_PORT;
|
2020-07-15 23:29:56 +02:00
|
|
|
|
|
|
|
|
if (toplayer) break; /* Stop after processing topmost layer */
|
2020-03-14 18:00:03 +01:00
|
|
|
}
|
|
|
|
|
DBCellClearDef(lc.lefYank);
|
|
|
|
|
lab->lab_flags |= PORT_VISITED;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
/* Check if any other ports belong to this pin */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
for (; lab != NULL; lab = lab->lab_next)
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
if (!(lab->lab_flags & PORT_VISITED))
|
|
|
|
|
if ((lab->lab_flags & PORT_NUM_MASK) == idx)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (lc.numWrites > 0)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN1 "END\n"); /* end of port geometries */
|
2020-03-14 18:00:03 +01:00
|
|
|
lc.numWrites = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LEFtext = MakeLegalLEFSyntax(reflab->lab_text);
|
2020-05-22 03:53:23 +02:00
|
|
|
if (lc.needHeader == FALSE)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "END %s\n", reflab->lab_text); /* end of pin */
|
2020-03-14 18:00:03 +01:00
|
|
|
if (LEFtext != reflab->lab_text) freeMagic(LEFtext);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
if (maxport >= 0)
|
|
|
|
|
{
|
2019-10-25 18:21:19 +02:00
|
|
|
/* Sanity check to see if port number is a duplicate. ONLY */
|
|
|
|
|
/* flag this if the other index has a different text, as it */
|
|
|
|
|
/* is perfectly legal to have multiple ports with the same */
|
|
|
|
|
/* name and index. */
|
|
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
for (tlab = reflab->lab_next; tlab != NULL; tlab = tlab->lab_next)
|
2017-08-02 04:14:42 +02:00
|
|
|
{
|
2019-10-25 18:21:19 +02:00
|
|
|
if (tlab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
if ((tlab->lab_flags & PORT_NUM_MASK) == idx)
|
2020-03-14 18:00:03 +01:00
|
|
|
if (strcmp(reflab->lab_text, tlab->lab_text))
|
2019-10-25 18:21:19 +02:00
|
|
|
{
|
|
|
|
|
TxError("Index %d is used for ports \"%s\" and \"%s\"\n",
|
2020-03-14 18:00:03 +01:00
|
|
|
idx, reflab->lab_text, tlab->lab_text);
|
2019-10-25 18:21:19 +02:00
|
|
|
idx--;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
else
|
2020-03-14 18:00:03 +01:00
|
|
|
lab = reflab->lab_next;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clear all PORT_VISITED bits in labels */
|
|
|
|
|
for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
lab->lab_flags &= ~(PORT_VISITED);
|
|
|
|
|
|
|
|
|
|
/* List of routing obstructions */
|
|
|
|
|
|
|
|
|
|
lc.lefMode = LEF_MODE_OBSTRUCT;
|
2017-08-02 04:14:42 +02:00
|
|
|
lc.lastType = TT_SPACE;
|
2020-05-22 22:14:27 +02:00
|
|
|
lc.needHeader = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Restrict to routing planes only */
|
|
|
|
|
|
2018-11-19 21:01:20 +01:00
|
|
|
if (hide)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2018-11-19 21:01:20 +01:00
|
|
|
/* If details of the cell are to be hidden, then first paint */
|
|
|
|
|
/* all route layers with an obstruction rectangle the size of */
|
|
|
|
|
/* the cell bounding box. Then recompute the label chunk */
|
|
|
|
|
/* regions used above to write the ports, expand each chunk by */
|
|
|
|
|
/* the route metal spacing width, and erase that area from the */
|
2020-03-14 18:00:03 +01:00
|
|
|
/* obstruction. For the obstruction boundary, find the extent */
|
|
|
|
|
/* of paint on the layer, not the LEF macro boundary, since */
|
|
|
|
|
/* paint may extend beyond the boundary, and sometimes the */
|
|
|
|
|
/* boundary may extend beyond the paint. To be done: make */
|
|
|
|
|
/* sure that every pin has a legal path to the outside of the */
|
|
|
|
|
/* cell. Otherwise, this routine can block internal pins. */
|
|
|
|
|
|
|
|
|
|
Rect layerBound;
|
2020-03-21 02:29:29 +01:00
|
|
|
labelLinkedList *thislll;
|
2018-11-19 21:01:20 +01:00
|
|
|
|
|
|
|
|
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
|
|
|
|
|
if (TTMaskHasType(&lmask, ttype))
|
2020-03-14 18:00:03 +01:00
|
|
|
{
|
|
|
|
|
layerBound.r_xbot = layerBound.r_xtop = 0;
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
if (TTMaskHasType(&DBPlaneTypes[pNum], ttype))
|
|
|
|
|
{
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatUse.cu_def->cd_planes[pNum],
|
2020-03-14 18:00:03 +01:00
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefGetBound, (ClientData)(&layerBound));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBPaint(lc.lefYank, &layerBound, ttype);
|
|
|
|
|
}
|
2018-11-19 21:01:20 +01:00
|
|
|
|
2020-03-21 02:29:29 +01:00
|
|
|
for (thislll = lll; thislll; thislll = thislll->lll_next)
|
2018-11-19 21:01:20 +01:00
|
|
|
{
|
2020-04-01 16:39:43 +02:00
|
|
|
int mspace;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-21 02:29:29 +01:00
|
|
|
lab = thislll->lll_label;
|
2018-11-19 21:01:20 +01:00
|
|
|
|
2020-04-01 16:39:43 +02:00
|
|
|
/* Look for wide spacing rules. If there are no wide spacing */
|
|
|
|
|
/* rules, then fall back on the default spacing rule. */
|
2020-04-08 02:08:42 +02:00
|
|
|
mspace = DRCGetDefaultWideLayerSpacing(lab->lab_type, (int)1E6);
|
2020-03-25 16:07:54 +01:00
|
|
|
if (mspace == 0)
|
|
|
|
|
mspace = DRCGetDefaultLayerSpacing(lab->lab_type, lab->lab_type);
|
|
|
|
|
|
2020-04-01 16:39:43 +02:00
|
|
|
thislll->lll_area.r_xbot -= mspace;
|
|
|
|
|
thislll->lll_area.r_ybot -= mspace;
|
|
|
|
|
thislll->lll_area.r_xtop += mspace;
|
|
|
|
|
thislll->lll_area.r_ytop += mspace;
|
2020-03-25 16:07:54 +01:00
|
|
|
|
|
|
|
|
DBErase(lc.lefYank, &thislll->lll_area, lab->lab_type);
|
2020-03-21 02:29:29 +01:00
|
|
|
freeMagic(thislll);
|
2018-11-19 21:01:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
|
|
|
|
lc.pNum = pNum;
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
2018-11-19 21:01:20 +01:00
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankGeometry, (ClientData) &lc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write all the geometry just generated */
|
|
|
|
|
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
2020-05-25 18:26:01 +02:00
|
|
|
lc.pNum = pNum;
|
2020-05-23 00:37:41 +02:00
|
|
|
DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
2017-04-25 14:41:48 +02:00
|
|
|
&TiPlaneRect, &lc.rmask,
|
|
|
|
|
lefWriteGeometry, (ClientData) &lc);
|
2020-05-25 18:26:01 +02:00
|
|
|
|
|
|
|
|
/* Additional yank & write for contacts (although ignore contacts for -hide) */
|
|
|
|
|
if (!hide)
|
|
|
|
|
{
|
|
|
|
|
lc.lefMode = LEF_MODE_OBS_CONTACT;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankContacts, (ClientData) &lc);
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
|
|
|
|
&TiPlaneRect, &lc.rmask,
|
|
|
|
|
lefWriteGeometry, (ClientData) &lc);
|
|
|
|
|
lc.lefMode = LEF_MODE_OBSTRUCT;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
if (lc.numWrites > 0)
|
2020-05-23 00:36:38 +02:00
|
|
|
fprintf(f, IN0 "END\n"); /* end of obstruction geometries */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-29 20:31:48 +02:00
|
|
|
/* If there are any properties saved in LEFproperties, write them out */
|
|
|
|
|
|
|
|
|
|
propvalue = (char *)DBPropGet(def, "LEFproperties", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
{
|
|
|
|
|
char *delim;
|
|
|
|
|
char *propfind = propvalue;
|
|
|
|
|
bool endfound = FALSE;
|
|
|
|
|
|
|
|
|
|
/* Properties are in space-separated key:value pairs. */
|
|
|
|
|
/* The value is in quotes and may contain spaces. */
|
|
|
|
|
/* One PROPERTY line is written per key:value pair. */
|
|
|
|
|
|
|
|
|
|
while (*propfind != '\0')
|
|
|
|
|
{
|
|
|
|
|
char dsave;
|
|
|
|
|
|
|
|
|
|
delim = propfind;
|
|
|
|
|
while (*delim != ' ' && *delim != '\0') delim++;
|
|
|
|
|
if (*delim == '\0') break;
|
|
|
|
|
while (*delim == ' ' && *delim != '\0') delim++;
|
|
|
|
|
if (*delim == '\0') break;
|
|
|
|
|
if (*delim == '\"')
|
|
|
|
|
{
|
|
|
|
|
delim++;
|
|
|
|
|
while (*delim != '\"' && *delim != '\0') delim++;
|
|
|
|
|
if (*delim == '\0') break;
|
|
|
|
|
delim++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
while (*delim != ' ' && *delim != '\0') delim++;
|
|
|
|
|
|
|
|
|
|
if (*delim == '\0') endfound = TRUE;
|
|
|
|
|
dsave = *delim;
|
|
|
|
|
*delim = '\0';
|
|
|
|
|
fprintf(f, IN0 "PROPERTY %s ;\n", propfind);
|
|
|
|
|
*delim = dsave;
|
|
|
|
|
if (endfound) break;
|
|
|
|
|
while (*delim == ' ' && *delim != '\0') delim++;
|
|
|
|
|
propfind = delim;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
fprintf(f, "END %s\n", def->cd_name); /* end of macro */
|
|
|
|
|
|
|
|
|
|
SigDisableInterrupts();
|
|
|
|
|
freeMagic(lc.lefMagicMap);
|
|
|
|
|
DBCellClearDef(lc.lefYank);
|
2017-08-02 04:14:42 +02:00
|
|
|
DBCellClearDef(lefFlatDef);
|
|
|
|
|
freeMagic(lefSourceUse.cu_id);
|
|
|
|
|
freeMagic(lefFlatUse.cu_id);
|
|
|
|
|
SelectClear();
|
2017-04-25 14:41:48 +02:00
|
|
|
SigEnableInterrupts();
|
|
|
|
|
|
|
|
|
|
UndoEnable();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-17 04:54:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefGetSites ---
|
|
|
|
|
*
|
|
|
|
|
* Pull SITE instances from multiple cells into a list of
|
|
|
|
|
* unique entries to be written to the LEF header of an
|
|
|
|
|
* output LEF file.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
lefGetSites(stackItem, i, clientData)
|
|
|
|
|
ClientData stackItem;
|
|
|
|
|
int i;
|
|
|
|
|
ClientData clientData;
|
|
|
|
|
{
|
|
|
|
|
CellDef *def = (CellDef *)stackItem;
|
|
|
|
|
HashTable *lefSiteTbl = (HashTable *)clientData;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
bool propfound;
|
|
|
|
|
char *propvalue;
|
|
|
|
|
|
|
|
|
|
propvalue = (char *)DBPropGet(def, "LEFsite", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
he = HashFind(lefSiteTbl, propvalue);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-29 22:12:09 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefGetProperties ---
|
|
|
|
|
*
|
|
|
|
|
* Pull property definitions from multiple cells into
|
|
|
|
|
* a list of unique entries to be written to the
|
|
|
|
|
* PROPERTYDEFINITIONS block in an output LEF file.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
lefGetProperties(stackItem, i, clientData)
|
|
|
|
|
ClientData stackItem;
|
|
|
|
|
int i;
|
|
|
|
|
ClientData clientData;
|
|
|
|
|
{
|
|
|
|
|
CellDef *def = (CellDef *)stackItem;
|
|
|
|
|
HashTable *lefPropTbl = (HashTable *)clientData;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
bool propfound;
|
|
|
|
|
char *propvalue;
|
|
|
|
|
|
|
|
|
|
propvalue = (char *)DBPropGet(def, "LEFproperties", &propfound);
|
|
|
|
|
if (propfound)
|
|
|
|
|
{
|
|
|
|
|
char *key;
|
|
|
|
|
char *psrch;
|
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
|
|
psrch = propvalue;
|
|
|
|
|
while (*psrch != '\0')
|
|
|
|
|
{
|
|
|
|
|
key = psrch;
|
|
|
|
|
while (*psrch != ' ' && *psrch != '\0') psrch++;
|
|
|
|
|
if (*psrch == '\0') break;
|
|
|
|
|
*psrch = '\0';
|
|
|
|
|
he = HashFind(lefPropTbl, key);
|
|
|
|
|
|
|
|
|
|
/* Potentially to do: Handle INT and REAL types */
|
|
|
|
|
/* For now, only STRING properties are handled. */
|
|
|
|
|
|
|
|
|
|
*psrch = ' ';
|
|
|
|
|
psrch++;
|
|
|
|
|
while (*psrch == ' ' && *psrch != '\0') psrch++;
|
|
|
|
|
value = psrch;
|
|
|
|
|
if (*psrch == '\0') break;
|
|
|
|
|
if (*psrch == '\"')
|
|
|
|
|
{
|
2020-05-29 22:51:20 +02:00
|
|
|
psrch++;
|
2020-05-29 22:12:09 +02:00
|
|
|
while (*psrch != '\"' && *psrch != '\0') psrch++;
|
|
|
|
|
if (*psrch == '\0') break;
|
|
|
|
|
psrch++;
|
|
|
|
|
}
|
|
|
|
|
else
|
2020-05-29 22:51:20 +02:00
|
|
|
{
|
|
|
|
|
psrch++;
|
2020-05-29 22:12:09 +02:00
|
|
|
while (*psrch != ' ' && *psrch != '\0') psrch++;
|
2020-05-29 22:51:20 +02:00
|
|
|
}
|
2020-05-29 22:12:09 +02:00
|
|
|
if (*psrch == '\0') break;
|
|
|
|
|
psrch++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* LefWriteAll --
|
|
|
|
|
*
|
|
|
|
|
* Write LEF-format output for each cell, beginning with
|
|
|
|
|
* the top-level cell use "rootUse".
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes a .lef file to disk.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2020-07-15 23:29:56 +02:00
|
|
|
LefWriteAll(rootUse, writeTopCell, lefTech, lefHide, lefTopLayer, recurse)
|
2017-04-25 14:41:48 +02:00
|
|
|
CellUse *rootUse;
|
|
|
|
|
bool writeTopCell;
|
2017-08-02 04:14:42 +02:00
|
|
|
bool lefTech;
|
2020-03-05 19:14:47 +01:00
|
|
|
bool lefHide;
|
2020-07-15 23:29:56 +02:00
|
|
|
bool lefTopLayer;
|
2019-12-19 16:33:22 +01:00
|
|
|
bool recurse;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-06-17 04:54:48 +02:00
|
|
|
HashTable propHashTbl, siteHashTbl;
|
2017-04-25 14:41:48 +02:00
|
|
|
CellDef *def, *rootdef;
|
|
|
|
|
FILE *f;
|
|
|
|
|
char *filename;
|
|
|
|
|
float scale = CIFGetOutputScale(1000); /* conversion to microns */
|
|
|
|
|
|
|
|
|
|
rootdef = rootUse->cu_def;
|
|
|
|
|
|
|
|
|
|
/* Make sure the entire subtree is read in */
|
|
|
|
|
DBCellReadArea(rootUse, &rootdef->cd_bbox);
|
|
|
|
|
|
|
|
|
|
/* Fix up bounding boxes if they've changed */
|
|
|
|
|
DBFixMismatch();
|
|
|
|
|
|
|
|
|
|
/* Mark all defs as being unvisited */
|
|
|
|
|
(void) DBCellSrDefs(0, lefDefInitFunc, (ClientData) 0);
|
|
|
|
|
|
|
|
|
|
/* Recursively visit all defs in the tree and push on stack */
|
2019-12-19 16:33:22 +01:00
|
|
|
/* If "recurse" is false, then only the children of the root use */
|
|
|
|
|
/* are pushed (this is the default behavior). */
|
2017-04-25 14:41:48 +02:00
|
|
|
lefDefStack = StackNew(100);
|
2019-12-19 16:33:22 +01:00
|
|
|
if (writeTopCell)
|
|
|
|
|
lefDefPushFunc(rootUse, (bool *)NULL);
|
|
|
|
|
DBCellEnum(rootUse->cu_def, lefDefPushFunc, (ClientData)&recurse);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Open the file for output */
|
|
|
|
|
|
|
|
|
|
f = lefFileOpen(rootdef, (char *)NULL, ".lef", "w", &filename);
|
|
|
|
|
|
|
|
|
|
TxPrintf("Generating LEF output %s for hierarchy rooted at cell %s:\n",
|
|
|
|
|
filename, rootdef->cd_name);
|
|
|
|
|
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
TxError("Cannot open output file %s (%s).\n", filename,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
#else
|
|
|
|
|
TxError("Cannot open output file: ");
|
|
|
|
|
perror(filename);
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-29 22:12:09 +02:00
|
|
|
/* For all cells, collect any properties */
|
2020-05-29 22:26:37 +02:00
|
|
|
HashInit(&propHashTbl, 4, HT_STRINGKEYS);
|
2020-05-29 22:12:09 +02:00
|
|
|
StackEnum(lefDefStack, lefGetProperties, &propHashTbl);
|
|
|
|
|
|
2020-06-17 04:54:48 +02:00
|
|
|
/* For all cells, collect any sites */
|
|
|
|
|
HashInit(&siteHashTbl, 4, HT_STRINGKEYS);
|
|
|
|
|
StackEnum(lefDefStack, lefGetSites, &siteHashTbl);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Now generate LEF output for all the cells we just found */
|
|
|
|
|
|
2020-06-17 04:54:48 +02:00
|
|
|
lefWriteHeader(rootdef, f, lefTech, &propHashTbl, &siteHashTbl);
|
2020-05-29 22:12:09 +02:00
|
|
|
|
|
|
|
|
HashKill(&propHashTbl);
|
2020-06-17 04:54:48 +02:00
|
|
|
HashKill(&siteHashTbl);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
while (def = (CellDef *) StackPop(lefDefStack))
|
|
|
|
|
{
|
|
|
|
|
def->cd_client = (ClientData) 0;
|
|
|
|
|
if (!SigInterruptPending)
|
2020-07-15 23:29:56 +02:00
|
|
|
lefWriteMacro(def, f, scale, lefHide, lefTopLayer);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2018-12-13 18:04:10 +01:00
|
|
|
/* End the LEF file */
|
2020-07-20 04:10:19 +02:00
|
|
|
fprintf(f, "END LIBRARY\n\n");
|
2018-12-13 18:04:10 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
fclose(f);
|
|
|
|
|
StackFree(lefDefStack);
|
|
|
|
|
}
|
2020-05-23 00:37:41 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* Function to initialize the client data field of all
|
|
|
|
|
* cell defs, in preparation for generating LEF output
|
|
|
|
|
* for a subtree rooted at a particular def.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-05-23 00:37:41 +02:00
|
|
|
int
|
2017-04-25 14:41:48 +02:00
|
|
|
lefDefInitFunc(def)
|
|
|
|
|
CellDef *def;
|
|
|
|
|
{
|
|
|
|
|
def->cd_client = (ClientData) 0;
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Function to push each cell def on lefDefStack
|
|
|
|
|
* if it hasn't already been pushed, and then recurse
|
|
|
|
|
* on all that def's children.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2019-12-19 16:33:22 +01:00
|
|
|
lefDefPushFunc(use, recurse)
|
2017-04-25 14:41:48 +02:00
|
|
|
CellUse *use;
|
2020-05-23 00:37:41 +02:00
|
|
|
bool *recurse;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
CellDef *def = use->cu_def;
|
|
|
|
|
|
|
|
|
|
if (def->cd_client || (def->cd_flags & CDINTERNAL))
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
def->cd_client = (ClientData) 1;
|
|
|
|
|
StackPush((ClientData) def, lefDefStack);
|
2019-12-19 16:33:22 +01:00
|
|
|
if (recurse && (*recurse))
|
|
|
|
|
(void) DBCellEnum(def, lefDefPushFunc, (ClientData)recurse);
|
2017-04-25 14:41:48 +02:00
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* LefWriteCell --
|
|
|
|
|
*
|
|
|
|
|
* Write LEF-format output for the indicated cell.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes a single .lef file to disk.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2020-07-15 23:29:56 +02:00
|
|
|
LefWriteCell(def, outName, isRoot, lefTech, lefHide, lefTopLayer)
|
2017-04-25 14:41:48 +02:00
|
|
|
CellDef *def; /* Cell being written */
|
|
|
|
|
char *outName; /* Name of output file, or NULL. */
|
|
|
|
|
bool isRoot; /* Is this the root cell? */
|
2017-08-02 04:14:42 +02:00
|
|
|
bool lefTech; /* Output layer information if TRUE */
|
2018-11-19 21:01:20 +01:00
|
|
|
bool lefHide; /* Hide detail other than pins if TRUE */
|
2020-07-15 23:29:56 +02:00
|
|
|
bool lefTopLayer; /* Use only topmost layer of pin if TRUE */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
char *filename;
|
|
|
|
|
FILE *f;
|
|
|
|
|
float scale = CIFGetOutputScale(1000);
|
|
|
|
|
|
|
|
|
|
f = lefFileOpen(def, outName, ".lef", "w", &filename);
|
|
|
|
|
|
|
|
|
|
TxPrintf("Generating LEF output %s for cell %s:\n", filename, def->cd_name);
|
|
|
|
|
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
TxError("Cannot open output file %s (%s).\n", filename,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
#else
|
|
|
|
|
TxError("Cannot open output file: ");
|
|
|
|
|
perror(filename);
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isRoot)
|
2020-05-29 22:12:09 +02:00
|
|
|
{
|
2020-06-17 04:54:48 +02:00
|
|
|
HashTable propHashTbl, siteHashTbl;
|
2020-05-29 22:12:09 +02:00
|
|
|
|
2020-05-29 22:36:07 +02:00
|
|
|
HashInit(&propHashTbl, 4, HT_STRINGKEYS);
|
2020-05-29 22:12:09 +02:00
|
|
|
lefGetProperties((ClientData)def, 0, (ClientData)&propHashTbl);
|
2020-06-17 04:54:48 +02:00
|
|
|
HashInit(&siteHashTbl, 4, HT_STRINGKEYS);
|
|
|
|
|
lefGetSites((ClientData)def, 0, (ClientData)&siteHashTbl);
|
|
|
|
|
lefWriteHeader(def, f, lefTech, &propHashTbl, &siteHashTbl);
|
2020-05-29 22:12:09 +02:00
|
|
|
HashKill(&propHashTbl);
|
2020-06-17 04:54:48 +02:00
|
|
|
HashKill(&siteHashTbl);
|
2020-05-29 22:12:09 +02:00
|
|
|
}
|
2020-07-15 23:29:56 +02:00
|
|
|
lefWriteMacro(def, f, scale, lefHide, lefTopLayer);
|
2020-07-20 04:10:19 +02:00
|
|
|
|
|
|
|
|
/* End the LEF file */
|
|
|
|
|
fprintf(f, "END LIBRARY\n\n");
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
|