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
|
2025-01-04 10:01:41 +01:00
|
|
|
static const 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"
|
2024-04-08 20:10:46 +02:00
|
|
|
#include "extract/extractInt.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"
|
|
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "select/select.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 *
|
2025-01-04 09:50:40 +01:00
|
|
|
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 *
|
2025-01-04 09:50:40 +01:00
|
|
|
lefFileOpen(
|
|
|
|
|
CellDef *def, /* Cell whose .lef file is to be written. Should
|
2017-04-25 14:41:48 +02:00
|
|
|
* be NULL if file is being opened for reading.
|
|
|
|
|
*/
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *file, /* If non-NULL, open 'name'.lef; otherwise,
|
2017-04-25 14:41:48 +02:00
|
|
|
* derive filename from 'def' as described
|
|
|
|
|
* above.
|
|
|
|
|
*/
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *suffix, /* Either ".lef" for LEF files or ".def" for DEF files */
|
|
|
|
|
const char *mode, /* Either "r" or "w", the mode in which the LEF/DEF
|
2017-04-25 14:41:48 +02:00
|
|
|
* file is to be opened.
|
|
|
|
|
*/
|
2025-01-04 09:50:40 +01:00
|
|
|
char **prealfile) /* If this is non-NULL, it gets set to point to
|
2017-04-25 14:41:48 +02:00
|
|
|
* a string holding the name of the LEF/DEF file.
|
|
|
|
|
*/
|
|
|
|
|
{
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *name, *ends, *endp;
|
|
|
|
|
char namebuf[512];
|
|
|
|
|
const char *locsuffix;
|
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++;
|
|
|
|
|
|
2024-10-04 18:23:02 +02:00
|
|
|
if ((endp = strrchr(ends, '.')))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefWriteHeader(
|
|
|
|
|
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 */
|
|
|
|
|
HashTable *siteTable) /* Hash table of sites used */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
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
|
|
|
|
2020-07-25 18:00:18 +02:00
|
|
|
if (lefTech)
|
|
|
|
|
{
|
|
|
|
|
fprintf(f, "UNITS\n");
|
|
|
|
|
fprintf(f, IN0 "DATABASE MICRONS %d ;\n", LEFdbUnits);
|
|
|
|
|
fprintf(f, "END UNITS\n");
|
|
|
|
|
fprintf(f, "\n");
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-29 22:12:09 +02:00
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
nprops = 0;
|
2024-10-04 18:23:02 +02:00
|
|
|
while ((he = HashNext(propTable, &hs)))
|
2020-05-29 22:12:09 +02:00
|
|
|
{
|
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 */
|
2025-01-04 10:05:56 +01:00
|
|
|
fprintf(f, IN0 "MACRO %s STRING ;\n", (const 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);
|
2024-10-04 18:23:02 +02:00
|
|
|
while ((he = HashNext(siteTable, &hs)))
|
2020-06-17 04:54:48 +02:00
|
|
|
{
|
|
|
|
|
/* Output the SITE as a macro */
|
|
|
|
|
CellDef *siteDef;
|
|
|
|
|
float scale;
|
|
|
|
|
char leffmt[2][10];
|
|
|
|
|
bool propfound;
|
|
|
|
|
char *propvalue;
|
|
|
|
|
Rect boundary;
|
|
|
|
|
|
2025-01-04 10:05:56 +01:00
|
|
|
siteDef = DBCellLookDef((const char *)he->h_key.h_name);
|
2020-06-17 04:54:48 +02:00
|
|
|
if (siteDef)
|
|
|
|
|
{
|
|
|
|
|
fprintf(f, "SITE %s\n", siteDef->cd_name);
|
|
|
|
|
|
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
|
|
|
propvalue = DBPropGetString(siteDef, "LEFsymmetry", &propfound);
|
2020-06-17 04:54:48 +02:00
|
|
|
if (propfound)
|
|
|
|
|
fprintf(f, IN0 "SYMMETRY %s ;\n", propvalue);
|
|
|
|
|
else
|
|
|
|
|
/* Usually core cells have symmetry Y only. */
|
|
|
|
|
fprintf(f, IN0 "SYMMETRY Y ;\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
|
|
|
propvalue = DBPropGetString(siteDef, "LEFclass", &propfound);
|
2020-06-17 04:54:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
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;
|
|
|
|
|
|
|
|
|
|
proprec = DBPropGet(def, "FIXED_BBOX", &propfound);
|
2020-06-17 04:54:48 +02:00
|
|
|
if (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
|
|
|
{
|
|
|
|
|
if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
|
|
|
|
|
(proprec->prop_len == 4))
|
|
|
|
|
{
|
|
|
|
|
boundary.r_xbot = proprec->prop_value.prop_integer[0];
|
|
|
|
|
boundary.r_ybot = proprec->prop_value.prop_integer[1];
|
|
|
|
|
boundary.r_xtop = proprec->prop_value.prop_integer[2];
|
|
|
|
|
boundary.r_ytop = proprec->prop_value.prop_integer[3];
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-17 04:54:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2024-10-04 18:23:02 +02:00
|
|
|
while ((he = HashNext(&LefInfo, &hs)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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);
|
2024-10-04 18:23:02 +02:00
|
|
|
while ((he = HashNext(&LefInfo, &hs)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefEraseGeometry(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo,
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData cdata)
|
2017-08-02 04:14:42 +02:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
2026-01-03 02:12:37 +01:00
|
|
|
otype = TiGetTypeExact(tile) | dinfo;
|
2017-10-06 04:08:24 +02:00
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefGetBound(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, /* (unused) */
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData cdata)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-30 22:29:37 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* lefHasPaint---
|
|
|
|
|
*
|
|
|
|
|
* Simple callback that returns 1 to stop the search when any tile matching
|
|
|
|
|
* the search mask is found.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-04 09:50:40 +01:00
|
|
|
lefHasPaint(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo,
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData clientData)
|
2022-11-30 22:29:37 +01:00
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefAccumulateArea(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, /* (unused) */
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData cdata)
|
2020-05-21 22:26:24 +02:00
|
|
|
{
|
|
|
|
|
int *area = (int *)cdata;
|
2026-01-03 02:12:37 +01:00
|
|
|
int locarea;
|
2020-05-21 22:26:24 +02:00
|
|
|
Rect rarea;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &rarea);
|
|
|
|
|
|
2026-01-03 02:12:37 +01:00
|
|
|
locarea = (rarea.r_xtop - rarea.r_xbot) * (rarea.r_ytop - rarea.r_ybot);
|
|
|
|
|
if (IsSplit(tile)) locarea /= 2;
|
|
|
|
|
*area += locarea;
|
2020-05-21 22:26:24 +02:00
|
|
|
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefFindTopmost(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo,
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData cdata)
|
2020-07-15 23:29:56 +02:00
|
|
|
{
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefYankGeometry(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo,
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData cdata)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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 */
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClient(tile) != CLIENTDEFAULT) return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2026-01-03 02:12:37 +01:00
|
|
|
otype = TiGetTypeExact(tile) | dinfo;
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefYankContacts(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, /* (unused) */
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData cdata)
|
2020-05-25 18:26:01 +02:00
|
|
|
{
|
|
|
|
|
lefClient *lefdata = (lefClient *)cdata;
|
|
|
|
|
Rect area;
|
2025-01-04 10:03:33 +01:00
|
|
|
TileType ttype, stype;
|
2020-05-25 18:26:01 +02:00
|
|
|
LefMapping *lefMagicToLefLayer;
|
2025-01-04 10:03:33 +01:00
|
|
|
TileTypeBitMask *lrmask;
|
2020-05-25 18:26:01 +02:00
|
|
|
|
|
|
|
|
/* Ignore marked tiles */
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClient(tile) != CLIENTDEFAULT) return 0;
|
2020-05-25 18:26:01 +02:00
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefWriteGeometry(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo,
|
2025-01-04 09:50:40 +01:00
|
|
|
ClientData cdata)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
lefClient *lefdata = (lefClient *)cdata;
|
|
|
|
|
FILE *f = lefdata->file;
|
|
|
|
|
float scale = lefdata->oscale;
|
2021-05-25 17:05:33 +02:00
|
|
|
char leffmt[6][16];
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType ttype, otype = TiGetTypeExact(tile) | dinfo;
|
2017-04-25 14:41:48 +02:00
|
|
|
LefMapping *lefMagicToLefLayer = lefdata->lefMagicMap;
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
/* Ignore tiles that have already been output */
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClient(tile) != CLIENTDEFAULT)
|
2017-08-02 04:14:42 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Mark this tile as visited */
|
2025-02-21 18:54:11 +01:00
|
|
|
TiSetClientINT(tile, 1);
|
2017-08-02 04:14:42 +02:00
|
|
|
|
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. */
|
|
|
|
|
|
2025-02-21 18:54:11 +01:00
|
|
|
TiSetClient(tile, CLIENTDEFAULT);
|
2020-05-22 03:53:23 +02:00
|
|
|
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 *
|
2025-01-04 09:50:40 +01:00
|
|
|
MakeLegalLEFSyntax(
|
|
|
|
|
char *text)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
static const char * const badLEFchars = ";# -*$\n";
|
|
|
|
|
char *cptr;
|
|
|
|
|
const char *bptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
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++)
|
2022-01-07 10:37:00 +01:00
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefWritePinHeader(
|
|
|
|
|
FILE *f,
|
|
|
|
|
Label *lab)
|
2020-05-22 03:53:23 +02:00
|
|
|
{
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefWriteMacro(
|
|
|
|
|
CellDef *def, /* Def for which to generate LEF output */
|
|
|
|
|
FILE *f, /* Output to this file */
|
|
|
|
|
float scale, /* Output distance units conversion factor */
|
|
|
|
|
int setback, /* If >= 0, hide all detail except pins inside setback */
|
|
|
|
|
int pinonly, /* If >= 0, only place pins where labels are defined */
|
|
|
|
|
bool toplayer, /* If TRUE, only output topmost layer of pins */
|
|
|
|
|
bool domaster) /* If TRUE, write masterslice layers */
|
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;
|
2020-10-07 22:26:56 +02:00
|
|
|
PlaneMask pmask;
|
2017-08-02 04:14:42 +02:00
|
|
|
SearchContext scx;
|
|
|
|
|
CellDef *lefFlatDef;
|
|
|
|
|
CellUse lefFlatUse, lefSourceUse;
|
2024-04-08 20:10:46 +02:00
|
|
|
TileTypeBitMask lmask, wmask, 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;
|
2020-08-01 03:45:42 +02:00
|
|
|
lefFlatUse.cu_parent = (CellDef *)NULL;
|
|
|
|
|
lefFlatUse.cu_xlo = 0;
|
|
|
|
|
lefFlatUse.cu_ylo = 0;
|
|
|
|
|
lefFlatUse.cu_xhi = 0;
|
|
|
|
|
lefFlatUse.cu_yhi = 0;
|
|
|
|
|
lefFlatUse.cu_xsep = 0;
|
|
|
|
|
lefFlatUse.cu_ysep = 0;
|
|
|
|
|
lefFlatUse.cu_client = (ClientData)CLIENTDEFAULT;
|
2017-08-02 04:14:42 +02:00
|
|
|
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;
|
2020-08-01 03:45:42 +02:00
|
|
|
lefSourceUse.cu_parent = (CellDef *)NULL;
|
|
|
|
|
lefSourceUse.cu_xlo = 0;
|
|
|
|
|
lefSourceUse.cu_ylo = 0;
|
|
|
|
|
lefSourceUse.cu_xhi = 0;
|
|
|
|
|
lefSourceUse.cu_yhi = 0;
|
|
|
|
|
lefSourceUse.cu_xsep = 0;
|
|
|
|
|
lefSourceUse.cu_ysep = 0;
|
|
|
|
|
lefSourceUse.cu_client = (ClientData)CLIENTDEFAULT;
|
2017-08-02 04:14:42 +02:00
|
|
|
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;
|
2020-12-04 03:37:32 +01:00
|
|
|
lc.lefMagicMap = defMakeInverseLayerMap(LAYER_MAP_NO_VIAS);
|
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);
|
2024-04-08 20:10:46 +02:00
|
|
|
TTMaskZero(&wmask);
|
2020-10-07 22:26:56 +02:00
|
|
|
pmask = 0;
|
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);
|
2024-10-04 18:23:02 +02:00
|
|
|
while ((he = HashNext(&LefInfo, &hs)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
lefLayer *lefl = (lefLayer *)HashGetValue(he);
|
2020-10-06 21:41:35 +02:00
|
|
|
if (lefl && (lefl->lefClass == CLASS_ROUTE || lefl->lefClass == CLASS_VIA
|
|
|
|
|
|| (domaster && lefl->lefClass == CLASS_MASTER)))
|
2020-10-07 22:26:56 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
2024-04-08 20:10:46 +02:00
|
|
|
else if (lefl->lefClass == CLASS_MASTER)
|
|
|
|
|
{
|
|
|
|
|
/* This is something of a hack. . . Record MASTERSLICE layers
|
|
|
|
|
* that are not substrate (e.g., "nwell" but not "pwell"). These
|
|
|
|
|
* will be used to paint obstructions on the plane of the
|
|
|
|
|
* masterslice layer. There may be better solutions to this.
|
|
|
|
|
*/
|
|
|
|
|
if (ExtCurStyle != NULL)
|
|
|
|
|
if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, lefl->type))
|
|
|
|
|
TTMaskSetType(&wmask, 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
|
|
|
|
2020-10-07 22:26:56 +02:00
|
|
|
if (domaster && lefl->lefClass == CLASS_MASTER)
|
|
|
|
|
pmask |= DBTypePlaneMaskTbl[lefl->type];
|
|
|
|
|
}
|
|
|
|
|
|
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. */
|
|
|
|
|
|
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
|
|
|
propvalue = DBPropGetString(def, "LEFclass", &propfound);
|
2017-04-25 14:41:48 +02:00
|
|
|
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)
|
|
|
|
|
{
|
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;
|
2019-11-13 21:10:01 +01:00
|
|
|
bool found;
|
|
|
|
|
|
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 = DBPropGet(def, "FIXED_BBOX", &found);
|
2019-11-13 21:10:01 +01:00
|
|
|
if (found)
|
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
|
|
|
{
|
|
|
|
|
if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
|
|
|
|
|
(proprec->prop_len == 4))
|
|
|
|
|
{
|
|
|
|
|
boundary.r_xbot = proprec->prop_value.prop_integer[0];
|
|
|
|
|
boundary.r_ybot = proprec->prop_value.prop_integer[1];
|
|
|
|
|
boundary.r_xtop = proprec->prop_value.prop_integer[2];
|
|
|
|
|
boundary.r_ytop = proprec->prop_value.prop_integer[3];
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-13 21:10:01 +01:00
|
|
|
}
|
|
|
|
|
|
2020-09-14 21:54:38 +02:00
|
|
|
/* Check if (boundry less setback) is degenerate. If so, then */
|
|
|
|
|
/* there is no effect of the "-hide" option. */
|
|
|
|
|
if (setback > 0)
|
|
|
|
|
{
|
|
|
|
|
if ((boundary.r_xtop - boundary.r_xbot) < (2 * setback)) setback = -1;
|
|
|
|
|
if ((boundary.r_ytop - boundary.r_ybot) < (2 * setback)) setback = -1;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
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
|
|
|
propvalue = DBPropGetString(def, "LEFsymmetry", &propfound);
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
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
|
|
|
propvalue = DBPropGetString(def, "LEFsite", &propfound);
|
2020-04-01 17:01:13 +02:00
|
|
|
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++;
|
2021-12-13 04:09:31 +01:00
|
|
|
idx = (int)lab->lab_port;
|
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))
|
2021-12-13 04:09:31 +01:00
|
|
|
if (lab->lab_port == idx)
|
2017-08-02 04:14:42 +02:00
|
|
|
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;
|
2023-06-13 17:28:46 +02:00
|
|
|
linkedNetName *lnn;
|
|
|
|
|
bool ignored;
|
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
|
|
|
|
2023-06-13 17:28:46 +02:00
|
|
|
ignored = FALSE;
|
2023-07-05 18:37:56 +02:00
|
|
|
|
|
|
|
|
// Ports that have been flagged as power or ground should not be
|
|
|
|
|
// checked for antenna diffusion and gate area.
|
|
|
|
|
|
|
|
|
|
if ((lab->lab_flags & PORT_DIR_MASK) != 0)
|
|
|
|
|
if (((lab->lab_flags & PORT_USE_MASK) == PORT_USE_POWER) ||
|
|
|
|
|
((lab->lab_flags & PORT_USE_MASK) == PORT_USE_GROUND))
|
2023-06-13 17:28:46 +02:00
|
|
|
ignored = TRUE;
|
|
|
|
|
|
2023-07-05 18:37:56 +02:00
|
|
|
// Check for net names to ignore for antenna checks.
|
|
|
|
|
if (!ignored)
|
|
|
|
|
for (lnn = lefIgnoreNets; lnn; lnn = lnn->lnn_next)
|
2024-05-12 02:48:35 +02:00
|
|
|
if (!strcmp(lnn->lnn_name, lab->lab_text) ||
|
|
|
|
|
!strcmp(lnn->lnn_name, "*"))
|
2023-07-05 18:37:56 +02:00
|
|
|
ignored = TRUE;
|
|
|
|
|
|
2023-06-13 17:28:46 +02:00
|
|
|
if (!ignored || (setback != 0))
|
|
|
|
|
SelectNet(&scx, lab->lab_type, 0, NULL, FALSE);
|
2023-04-26 16:24:13 +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.
|
|
|
|
|
|
|
|
|
|
antgatearea = 0;
|
2023-06-13 17:28:46 +02:00
|
|
|
if (!ignored)
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
2023-04-26 16:24:13 +02:00
|
|
|
&TiPlaneRect, &gatetypemask,
|
|
|
|
|
lefAccumulateArea, (ClientData) &antgatearea);
|
2023-06-13 17:28:46 +02:00
|
|
|
// Stop after first plane with geometry to avoid double-counting
|
|
|
|
|
// contacts.
|
|
|
|
|
if (antgatearea > 0) break;
|
|
|
|
|
}
|
2023-04-26 16:24:13 +02:00
|
|
|
|
|
|
|
|
antdiffarea = 0;
|
2023-06-13 17:28:46 +02:00
|
|
|
if (!ignored)
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
2023-04-26 16:24:13 +02:00
|
|
|
&TiPlaneRect, &difftypemask,
|
|
|
|
|
lefAccumulateArea, (ClientData) &antdiffarea);
|
2023-06-13 17:28:46 +02:00
|
|
|
// Stop after first plane with geometry to avoid double-counting
|
|
|
|
|
// contacts.
|
|
|
|
|
if (antdiffarea > 0) break;
|
|
|
|
|
}
|
2023-04-26 16:24:13 +02:00
|
|
|
|
2021-04-20 19:33:25 +02:00
|
|
|
if (setback == 0)
|
2020-03-14 18:00:03 +01:00
|
|
|
{
|
2020-03-21 02:29:29 +01:00
|
|
|
Rect carea;
|
|
|
|
|
labelLinkedList *newlll;
|
|
|
|
|
|
2024-03-08 18:28:29 +01:00
|
|
|
/* SelectClear() requires SelectRootDef be non-NULL, although
|
|
|
|
|
* this might be an error in SelectClear(), since SelectClear()
|
|
|
|
|
* clears the selection, not the source.
|
|
|
|
|
*/
|
|
|
|
|
if (SelectRootDef == NULL) SelectRootDef = lefFlatDef;
|
2023-04-26 16:24:13 +02:00
|
|
|
SelectClear();
|
2021-04-20 19:33:25 +02:00
|
|
|
if (pinonly == 0)
|
2021-04-20 16:19:12 +02:00
|
|
|
carea = labr;
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-09-11 20:34:42 +02:00
|
|
|
scx.scx_area = labr;
|
2021-04-20 19:33:25 +02:00
|
|
|
SelectChunk(&scx, lab->lab_type, 0, &carea, FALSE);
|
2023-09-11 20:34:42 +02:00
|
|
|
if (GEO_RECTNULL(&carea))
|
|
|
|
|
{
|
|
|
|
|
/* In the flattened cell, any connected layer
|
|
|
|
|
* could be underneath the label.
|
|
|
|
|
*/
|
|
|
|
|
TileType tt;
|
|
|
|
|
for (tt = TT_TECHDEPBASE; tt < DBNumTypes; tt++)
|
|
|
|
|
{
|
|
|
|
|
if (tt == lab->lab_type) continue;
|
|
|
|
|
if (DBConnectsTo(tt, lab->lab_type))
|
|
|
|
|
{
|
|
|
|
|
scx.scx_area = labr;
|
|
|
|
|
SelectChunk(&scx, tt, 0, &carea, FALSE);
|
|
|
|
|
if (!GEO_RECTNULL(&carea))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (tt == DBNumTypes)
|
|
|
|
|
carea = labr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((pinonly > 0) && (!GEO_RECTNULL(&carea)))
|
2021-04-20 19:33:25 +02:00
|
|
|
{
|
|
|
|
|
Rect psetback;
|
|
|
|
|
GEO_EXPAND(&boundary, -pinonly, &psetback);
|
2021-10-09 19:44:04 +02:00
|
|
|
SelRemoveArea(&psetback, &DBAllButSpaceAndDRCBits, NULL);
|
2021-04-20 19:33:25 +02:00
|
|
|
}
|
2021-04-20 16:19:12 +02:00
|
|
|
}
|
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);
|
2022-09-23 21:44:49 +02:00
|
|
|
if (pNum >= 0)
|
|
|
|
|
DBPaintPlane(SelectDef->cd_planes[pNum], &carea,
|
|
|
|
|
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
|
|
|
}
|
2020-09-14 21:54:38 +02:00
|
|
|
else if (setback > 0)
|
|
|
|
|
{
|
|
|
|
|
Rect carea;
|
|
|
|
|
|
|
|
|
|
/* For -hide with setback, select the entire net and then */
|
2021-04-20 16:19:12 +02:00
|
|
|
/* remove the part inside the setback area. */
|
2020-09-14 21:54:38 +02:00
|
|
|
|
|
|
|
|
GEO_EXPAND(&boundary, -setback, &carea);
|
2021-10-09 19:44:04 +02:00
|
|
|
SelRemoveArea(&carea, &DBAllButSpaceAndDRCBits, NULL);
|
2021-04-20 16:19:12 +02:00
|
|
|
|
2021-04-20 19:33:25 +02:00
|
|
|
/* Apply any additional setback from the "-pinonly" option */
|
|
|
|
|
if (pinonly > setback)
|
|
|
|
|
{
|
|
|
|
|
Rect psetback;
|
|
|
|
|
GEO_EXPAND(&boundary, -pinonly, &psetback);
|
2021-10-09 19:44:04 +02:00
|
|
|
SelRemoveArea(&psetback, &DBAllButSpaceAndDRCBits, NULL);
|
2021-04-20 19:33:25 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-20 16:19:12 +02:00
|
|
|
/* Paint over the label area so that labels do not simply */
|
|
|
|
|
/* disappear by being inside the setback area. */
|
|
|
|
|
|
|
|
|
|
pNum = DBPlane(lab->lab_type);
|
2024-06-04 16:07:43 +02:00
|
|
|
if (pNum >= 0) // ignore labels in space
|
|
|
|
|
DBPaintPlane(SelectDef->cd_planes[pNum], &labr,
|
|
|
|
|
DBStdPaintTbl(lab->lab_type, pNum),
|
|
|
|
|
(PaintUndoInfo *) NULL);
|
2020-09-14 21:54:38 +02:00
|
|
|
}
|
2020-03-14 18:00:03 +01:00
|
|
|
else
|
2021-04-20 19:33:25 +02:00
|
|
|
{
|
|
|
|
|
/* Apply any pin setback */
|
|
|
|
|
if (pinonly >= 0)
|
|
|
|
|
{
|
|
|
|
|
Rect psetback;
|
|
|
|
|
GEO_EXPAND(&boundary, -pinonly, &psetback);
|
2021-10-09 19:44:04 +02:00
|
|
|
SelRemoveArea(&psetback, &DBAllButSpaceAndDRCBits, NULL);
|
2021-04-20 19:33:25 +02:00
|
|
|
|
|
|
|
|
/* Paint over the label area so that labels do not simply */
|
|
|
|
|
/* disappear by being inside the setback area. */
|
|
|
|
|
|
|
|
|
|
pNum = DBPlane(lab->lab_type);
|
2024-06-04 16:07:43 +02:00
|
|
|
if (pNum >= 0) // ignore labels in space
|
|
|
|
|
DBPaintPlane(SelectDef->cd_planes[pNum], &labr,
|
|
|
|
|
DBStdPaintTbl(lab->lab_type, pNum),
|
|
|
|
|
(PaintUndoInfo *) NULL);
|
2021-04-20 19:33:25 +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 */
|
2020-10-07 22:26:56 +02:00
|
|
|
/* obstructions. Masterslice layers are considered an */
|
|
|
|
|
/* exception, as they are often needed for ensuring */
|
|
|
|
|
/* connectivity between power supply and wells. */
|
|
|
|
|
|
|
|
|
|
if (toplayer && (pNum != pTop))
|
|
|
|
|
{
|
2025-02-23 03:15:45 +01:00
|
|
|
if (domaster && (pmask != 0))
|
2020-10-07 22:26:56 +02:00
|
|
|
{
|
2020-10-07 22:34:49 +02:00
|
|
|
if (!PlaneMaskHasPlane(pmask, pNum))
|
2020-10-07 22:26:56 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else continue;
|
|
|
|
|
}
|
2020-07-15 23:29:56 +02:00
|
|
|
|
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-03-14 18:00:03 +01:00
|
|
|
}
|
2021-11-12 17:34:16 +01:00
|
|
|
|
|
|
|
|
/* Check if any other ports are already contained in this selection. */
|
|
|
|
|
/* If so, mark them as visited. Use lefFindTopmost(), which is just a */
|
|
|
|
|
/* routine that stops the search by returning 1 when something is found */
|
|
|
|
|
|
|
|
|
|
for (tlab = lab->lab_next; tlab != (Label *)NULL; tlab = tlab->lab_next)
|
|
|
|
|
if (tlab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
if (!(tlab->lab_flags & PORT_VISITED))
|
2021-12-13 04:09:31 +01:00
|
|
|
if (tlab->lab_port == idx)
|
2021-11-12 17:34:16 +01:00
|
|
|
{
|
2022-01-24 23:19:13 +01:00
|
|
|
TileTypeBitMask lmask, smask;
|
2021-11-12 17:34:16 +01:00
|
|
|
TTMaskSetOnlyType(&lmask, tlab->lab_type);
|
2022-01-24 23:19:13 +01:00
|
|
|
|
|
|
|
|
/* If label is a "sticky" label, then contacts with the */
|
|
|
|
|
/* type as residue must be added to the layer mask. */
|
|
|
|
|
if (tlab->lab_flags & LABEL_STICKY)
|
|
|
|
|
{
|
|
|
|
|
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
|
|
|
|
|
{
|
|
|
|
|
if (DBIsContact(ttype))
|
|
|
|
|
{
|
|
|
|
|
DBFullResidueMask(ttype, &smask);
|
|
|
|
|
if (TTMaskHasType(&smask, tlab->lab_type))
|
|
|
|
|
TTMaskSetType(&lmask, ttype);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-12 17:34:16 +01:00
|
|
|
pNum = DBPlane(tlab->lab_type);
|
|
|
|
|
if (DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
|
|
|
|
&tlab->lab_rect, &lmask,
|
|
|
|
|
lefFindTopmost, (ClientData)NULL))
|
|
|
|
|
tlab->lab_flags |= PORT_VISITED;
|
|
|
|
|
|
|
|
|
|
/* For the "toplayer" option, ports on lower layers will not be */
|
|
|
|
|
/* in the yank buffer but will still be in the selection. */
|
|
|
|
|
else if (toplayer)
|
|
|
|
|
if (DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[pNum],
|
|
|
|
|
&tlab->lab_rect, &lmask,
|
|
|
|
|
lefFindTopmost, (ClientData)NULL))
|
|
|
|
|
tlab->lab_flags |= PORT_VISITED;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
DBCellClearDef(lc.lefYank);
|
|
|
|
|
lab->lab_flags |= PORT_VISITED;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-11-12 17:34:16 +01:00
|
|
|
/* Check if any other unvisited 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))
|
2021-12-13 04:09:31 +01:00
|
|
|
if (lab->lab_port == idx)
|
2020-03-14 18:00:03 +01:00
|
|
|
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)
|
2021-12-13 04:09:31 +01:00
|
|
|
if (tlab->lab_port == 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
|
|
|
|
2024-04-08 20:10:46 +02:00
|
|
|
/* Restrict to routing and masterslice planes only */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-09-14 21:54:38 +02:00
|
|
|
if (setback >= 0)
|
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. */
|
|
|
|
|
|
2022-11-30 22:29:37 +01:00
|
|
|
Rect layerBound, manualBound;
|
2020-03-21 02:29:29 +01:00
|
|
|
labelLinkedList *thislll;
|
2022-11-30 22:29:37 +01:00
|
|
|
bool 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;
|
2022-11-30 22:29:37 +01:00
|
|
|
|
|
|
|
|
/* If there is a property OBS_BBOX, then use the value of the */
|
|
|
|
|
/* defined box to set the minimum hidden area. This will still */
|
|
|
|
|
/* get clipped to the setback. */
|
|
|
|
|
|
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 = DBPropGet(def, "OBS_BBOX", &propfound);
|
2022-11-30 22:29:37 +01:00
|
|
|
if (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
|
|
|
if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
|
|
|
|
|
(proprec->prop_len == 4))
|
|
|
|
|
{
|
|
|
|
|
manualBound.r_xbot = proprec->prop_value.prop_integer[0];
|
|
|
|
|
manualBound.r_ybot = proprec->prop_value.prop_integer[1];
|
|
|
|
|
manualBound.r_xtop = proprec->prop_value.prop_integer[2];
|
|
|
|
|
manualBound.r_ytop = proprec->prop_value.prop_integer[3];
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-11-30 22:29:37 +01:00
|
|
|
{
|
|
|
|
|
TxError("Improper values for obstruction bounding box "
|
|
|
|
|
"OBS_BBOX property");
|
|
|
|
|
manualBound = GeoNullRect;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
manualBound = GeoNullRect;
|
2018-11-19 21:01:20 +01:00
|
|
|
|
|
|
|
|
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
|
2024-04-08 20:10:46 +02:00
|
|
|
if (TTMaskHasType(&lmask, ttype) || TTMaskHasType(&wmask, ttype))
|
2020-03-14 18:00:03 +01:00
|
|
|
{
|
2020-09-14 21:54:38 +02:00
|
|
|
Rect r;
|
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));
|
2022-11-30 22:29:37 +01:00
|
|
|
|
|
|
|
|
/* Add any manual boundary if there is any */
|
|
|
|
|
/* material at all in the cell on this plane. */
|
|
|
|
|
if (!GEO_RECTNULL(&manualBound))
|
|
|
|
|
if (DBSrPaintArea((Tile *)NULL, def->cd_planes[pNum],
|
|
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefHasPaint, (ClientData)NULL) == 1)
|
|
|
|
|
GeoInclude(&manualBound, &layerBound);
|
2020-03-14 18:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
2020-09-14 21:54:38 +02:00
|
|
|
/* Clip layerBound to setback boundary */
|
|
|
|
|
GEO_EXPAND(&boundary, -setback, &r);
|
|
|
|
|
GeoClip(&layerBound, &r);
|
|
|
|
|
|
2020-03-14 18:00:03 +01:00
|
|
|
DBPaint(lc.lefYank, &layerBound, ttype);
|
|
|
|
|
}
|
2018-11-19 21:01:20 +01:00
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
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);
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, thislll);
|
2018-11-19 21:01:20 +01:00
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2020-09-14 21:54:38 +02:00
|
|
|
|
2020-12-18 22:38:06 +01:00
|
|
|
if (setback >= 0)
|
2020-09-14 21:54:38 +02:00
|
|
|
{
|
|
|
|
|
/* For -hide with setback, yank everything in the area outside */
|
|
|
|
|
/* the setback. */
|
|
|
|
|
|
|
|
|
|
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
|
|
|
|
Rect r;
|
|
|
|
|
lc.pNum = pNum;
|
|
|
|
|
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ytop = boundary.r_ybot + setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankGeometry, (ClientData) &lc);
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ybot = boundary.r_ytop - setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankGeometry, (ClientData) &lc);
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ybot = boundary.r_ybot + setback;
|
|
|
|
|
r.r_ytop = boundary.r_ytop - setback;
|
|
|
|
|
r.r_xtop = boundary.r_xbot + setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankGeometry, (ClientData) &lc);
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ybot = boundary.r_ybot + setback;
|
|
|
|
|
r.r_ytop = boundary.r_ytop - setback;
|
|
|
|
|
r.r_xbot = boundary.r_xtop - setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankGeometry, (ClientData) &lc);
|
|
|
|
|
}
|
|
|
|
|
}
|
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) */
|
2020-09-14 21:54:38 +02:00
|
|
|
if (setback < 0)
|
2020-05-25 18:26:01 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-09-14 21:54:38 +02:00
|
|
|
else if (setback > 0)
|
|
|
|
|
{
|
|
|
|
|
Rect r;
|
|
|
|
|
|
|
|
|
|
/* Apply only to area outside setback. */
|
|
|
|
|
lc.lefMode = LEF_MODE_OBS_CONTACT;
|
|
|
|
|
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ytop = boundary.r_ybot + setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankContacts, (ClientData) &lc);
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
|
|
|
|
&r, &lc.rmask, lefWriteGeometry, (ClientData) &lc);
|
|
|
|
|
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ybot = boundary.r_ytop - setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankContacts, (ClientData) &lc);
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
|
|
|
|
&r, &lc.rmask, lefWriteGeometry, (ClientData) &lc);
|
|
|
|
|
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ybot = boundary.r_ybot + setback;
|
|
|
|
|
r.r_ytop = boundary.r_ytop - setback;
|
|
|
|
|
r.r_xtop = boundary.r_xbot + setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankContacts, (ClientData) &lc);
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
|
|
|
|
&r, &lc.rmask, lefWriteGeometry, (ClientData) &lc);
|
|
|
|
|
|
|
|
|
|
r = def->cd_bbox;
|
|
|
|
|
r.r_ybot = boundary.r_ybot + setback;
|
|
|
|
|
r.r_ytop = boundary.r_ytop - setback;
|
|
|
|
|
r.r_xbot = boundary.r_xtop - setback;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lefFlatDef->cd_planes[pNum],
|
|
|
|
|
&r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
lefYankContacts, (ClientData) &lc);
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, lc.lefYank->cd_planes[pNum],
|
|
|
|
|
&r, &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 */
|
|
|
|
|
|
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
|
|
|
propvalue = DBPropGetString(def, "LEFproperties", &propfound);
|
2020-05-29 20:31:48 +02:00
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefGetSites(
|
|
|
|
|
ClientData stackItem,
|
|
|
|
|
int i,
|
|
|
|
|
ClientData clientData)
|
2020-06-17 04:54:48 +02:00
|
|
|
{
|
|
|
|
|
CellDef *def = (CellDef *)stackItem;
|
|
|
|
|
HashTable *lefSiteTbl = (HashTable *)clientData;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
bool propfound;
|
|
|
|
|
char *propvalue;
|
|
|
|
|
|
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
|
|
|
propvalue = DBPropGetString(def, "LEFsite", &propfound);
|
2020-06-17 04:54:48 +02:00
|
|
|
if (propfound)
|
2025-01-04 10:04:49 +01:00
|
|
|
he = HashFind(lefSiteTbl, propvalue); /* FIXME return value not used from call to function with no side-effects (reevaluate this entire func purpose?) */
|
2020-06-17 04:54:48 +02:00
|
|
|
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefGetProperties(
|
|
|
|
|
ClientData stackItem,
|
|
|
|
|
int i,
|
|
|
|
|
ClientData clientData)
|
2020-05-29 22:12:09 +02:00
|
|
|
{
|
|
|
|
|
CellDef *def = (CellDef *)stackItem;
|
|
|
|
|
HashTable *lefPropTbl = (HashTable *)clientData;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
bool propfound;
|
|
|
|
|
char *propvalue;
|
|
|
|
|
|
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
|
|
|
propvalue = DBPropGetString(def, "LEFproperties", &propfound);
|
2020-05-29 22:12:09 +02:00
|
|
|
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';
|
2025-01-04 10:04:49 +01:00
|
|
|
he = HashFind(lefPropTbl, key); /* FIXME return value not used from call to function with no side-effects */
|
2020-05-29 22:12:09 +02:00
|
|
|
|
|
|
|
|
/* 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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefWriteAll(
|
|
|
|
|
CellUse *rootUse,
|
|
|
|
|
bool writeTopCell,
|
|
|
|
|
bool lefTech,
|
|
|
|
|
int lefHide,
|
|
|
|
|
int lefPinOnly,
|
|
|
|
|
bool lefTopLayer,
|
|
|
|
|
bool lefDoMaster,
|
|
|
|
|
bool recurse)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-06-17 04:54:48 +02:00
|
|
|
HashTable propHashTbl, siteHashTbl;
|
2024-04-29 23:43:37 +02:00
|
|
|
CellDef *def, *rootdef, *err_def;
|
2017-04-25 14:41:48 +02:00
|
|
|
FILE *f;
|
|
|
|
|
char *filename;
|
|
|
|
|
float scale = CIFGetOutputScale(1000); /* conversion to microns */
|
|
|
|
|
|
|
|
|
|
rootdef = rootUse->cu_def;
|
|
|
|
|
|
|
|
|
|
/* Make sure the entire subtree is read in */
|
2024-04-29 23:43:37 +02:00
|
|
|
err_def = DBCellReadArea(rootUse, &rootdef->cd_bbox, TRUE);
|
|
|
|
|
if (err_def != NULL)
|
2021-01-14 21:21:39 +01:00
|
|
|
{
|
|
|
|
|
TxError("Could not read entire subtree of the cell.\n");
|
2024-04-29 23:43:37 +02:00
|
|
|
TxError("Failed on cell %s.\n", err_def->cd_name);
|
2021-01-14 21:21:39 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
2024-10-04 18:23:02 +02:00
|
|
|
while ((def = (CellDef *) StackPop(lefDefStack)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
def->cd_client = (ClientData) 0;
|
|
|
|
|
if (!SigInterruptPending)
|
2021-04-20 16:19:12 +02:00
|
|
|
lefWriteMacro(def, f, scale, lefHide, lefPinOnly, lefTopLayer, lefDoMaster);
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefDefInitFunc(
|
|
|
|
|
CellDef *def)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
lefDefPushFunc(
|
|
|
|
|
CellUse *use,
|
|
|
|
|
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
|
2025-01-04 09:50:40 +01:00
|
|
|
LefWriteCell(
|
|
|
|
|
CellDef *def, /* Cell being written */
|
|
|
|
|
char *outName, /* Name of output file, or NULL. */
|
|
|
|
|
bool isRoot, /* Is this the root cell? */
|
|
|
|
|
bool lefTech, /* Output layer information if TRUE */
|
|
|
|
|
int lefHide, /* Hide detail other than pins if >= 0 */
|
|
|
|
|
int lefPinOnly, /* Only generate pins on label areas */
|
|
|
|
|
bool lefTopLayer, /* Use only topmost layer of pin if TRUE */
|
|
|
|
|
bool lefDoMaster) /* Write masterslice layers 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
|
|
|
}
|
2021-04-20 16:19:12 +02:00
|
|
|
lefWriteMacro(def, f, scale, lefHide, lefPinOnly, lefTopLayer, lefDoMaster);
|
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);
|
|
|
|
|
}
|
|
|
|
|
|