mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-08-29 17:39:28 +02:00
for the DEF file contents, and loads the cell into the layout window when done. That makes it consistent with other similar functions such as "gds read". Existing scripts which create (by loading) a new cell before reading the DEF should not be affected by the change.
561 lines
15 KiB
C
561 lines
15 KiB
C
/*
|
|
* lefCmd.c --
|
|
*
|
|
* Commands for the LEF module only.
|
|
*
|
|
*/
|
|
|
|
#ifndef lint
|
|
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/lef/lefCmd.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <time.h>
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "windows/windows.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "utils/main.h"
|
|
#include "utils/utils.h"
|
|
#include "utils/malloc.h"
|
|
#include "textio/txcommands.h"
|
|
#include "commands/commands.h"
|
|
|
|
/* C99 compat */
|
|
#include "textio/textio.h"
|
|
#include "cif/cif.h"
|
|
#include "lef/lefInt.h"
|
|
|
|
int lefDateStamp = -1; /* If not -1, defines the timestamp to use when creating
|
|
* new cell defs from LEF or DEF. Useful when generating
|
|
* libraries to make sure that full and abstract views of
|
|
* the same cell have matching timestamps.
|
|
*/
|
|
|
|
linkedNetName *lefIgnoreNets = NULL; /* Nets names to ignore for antenna area */
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* CmdLef --
|
|
*
|
|
* Implement the "lef" and "def" commands: generate LEF-format output
|
|
* for a layout, or read LEF- or DEF-format files.
|
|
*
|
|
* Usage:
|
|
* lef [options]
|
|
* def [options]
|
|
*
|
|
* Results:
|
|
* Always return 0.
|
|
*
|
|
* Side effects:
|
|
* Generates LEF-format output on disk somewhere, or reads
|
|
* LEF- or DEF-format files, generating cell definitions and
|
|
* uses in the process.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/* These definitions must correspond to the ordering in cmdLefOption[] below */
|
|
|
|
#define LEF_READ 0
|
|
#define LEF_WRITE 1
|
|
#define LEF_WRITEALL 2
|
|
#define LEF_NOCHECK 3
|
|
#define LEF_DATESTAMP 4
|
|
#define LEF_HELP 5
|
|
|
|
void
|
|
CmdLef(
|
|
MagWindow *w,
|
|
TxCommand *cmd)
|
|
{
|
|
int option, i, cargs, units = 1000; /* Default nanometers */
|
|
const char * const *msg;
|
|
char *namep;
|
|
CellUse *selectedUse;
|
|
CellDef *selectedDef;
|
|
bool is_lef;
|
|
bool lefImport = FALSE; /* Indicates whether or not we
|
|
* read cells from .mag files
|
|
* when the FOREIGN statement
|
|
* is encountered in a macro.
|
|
*/
|
|
bool lefAnnotate = FALSE; /* Indicates that no celldefs should be
|
|
* created from any LEF files, which
|
|
* will be used for annotation only.
|
|
*/
|
|
bool lefTopCell = TRUE; /* Indicates whether or not we
|
|
* write the top-level cell to
|
|
* LEF, or just the subcells.
|
|
*/
|
|
bool allSpecial = FALSE; /* Indicates whether we should
|
|
* treat all geometry as "special"
|
|
* nets in DEF format output.
|
|
*/
|
|
bool lefTech = FALSE; /* Indicates that technology info
|
|
* will be output along with the
|
|
* lef macro.
|
|
*/
|
|
int lefHide = -1; /* If >= 0, hide all details of the macro
|
|
* other than pin area surrounding labels,
|
|
* with the indicated setback distance.
|
|
*/
|
|
int lefPinOnly = -1; /* If >= 0, make pins only where labels
|
|
* are defined, not the whole net. Values
|
|
* > 0 limit how far pins can extend into
|
|
* the interior of the cell.
|
|
*/
|
|
bool lefTopLayer = FALSE; /* If TRUE, only output the topmost
|
|
* layer used by a pin, and make
|
|
* all layers below it obstructions.
|
|
*/
|
|
bool lefDoMaster = TRUE; /* If TRUE, output masterslice layers;
|
|
* If FALSE, ignore masterslice layers.
|
|
*/
|
|
bool recurse = FALSE; /* If TRUE, recurse on all subcells
|
|
* during "writeall". By default,
|
|
* only the immediate children of the
|
|
* top level cell are output.
|
|
*/
|
|
bool defLabelNets = FALSE; /* If TRUE, attach a label to the
|
|
* center of the first rectangle
|
|
* found on that net.
|
|
*/
|
|
bool defAnnotate = FALSE; /* Indicates that no geometry should be
|
|
* created from any DEF files, which
|
|
* will be used for label annotation only.
|
|
*/
|
|
bool defNoBlockage = FALSE; /* Indicates that BLOCKAGE geometry in
|
|
* the DEF file should be ignored; only
|
|
* mask geometry will be generated.
|
|
*/
|
|
bool defAnalRetentive = FALSE; /* Deal with situations where tools
|
|
* have interpreted ambiguities in the
|
|
* LEF/DEF spec in the most stupid way
|
|
* possible, and then coded the rule
|
|
* in such a way that the tool crashes
|
|
* because the developers did not
|
|
* entertain the idea that a more
|
|
* sensible interpretation was possible.
|
|
*/
|
|
|
|
static const char * const cmdLefOption[] =
|
|
{
|
|
"read [filename] read a LEF file filename[.lef]\n"
|
|
" read [filename] -import read a LEF file; import cells from .mag files\n"
|
|
" read [filename] -annotate read a LEF file for cell annotation only.",
|
|
"write [filename] [-tech] write LEF for current cell\n"
|
|
" write [filename] -hide hide all details other than ports\n"
|
|
" write [filename] -hide <d> hide details in area set back distance <d>",
|
|
"writeall write all cells including the top-level cell\n"
|
|
" writeall -notop write all children of the top-level cell\n"
|
|
" writeall -all recurse on all subcells of the top-level cell\n"
|
|
" writeall -hide hide all details other than ports\n"
|
|
" writeall -hide [dist] hide details in area set back distance dist",
|
|
"nocheck [netname ...] ignore antenna area checks on named net(s)",
|
|
"datestamp [value] force the timestamp of cells read from LEF",
|
|
"help print this help information",
|
|
NULL
|
|
};
|
|
|
|
static const char * const cmdDefOption[] =
|
|
{
|
|
"read [filename] read a DEF file filename[.def]\n"
|
|
" read [filename] -labels read a DEF file with net labeling\n"
|
|
" read [filename] -annotate read a DEF file for net annotation only\n",
|
|
" read [filename] -noblockage read a DEF file (mask layers only).",
|
|
"write [cell] [-allspecial] write DEF for current or indicated cell",
|
|
"writeall (use \"flatten -nosubckt\" + \"def"
|
|
" write\" instead)",
|
|
"help print this help information",
|
|
NULL
|
|
};
|
|
|
|
/* Determine whether this function has been called via the "lef" or
|
|
* the "def" command.
|
|
*/
|
|
is_lef = (cmd->tx_argv[0][0] == 'd') ? FALSE : TRUE;
|
|
|
|
if (cmd->tx_argc < 2)
|
|
option = LEF_HELP;
|
|
else
|
|
{
|
|
option = Lookup(cmd->tx_argv[1], cmdLefOption);
|
|
if (option < 0)
|
|
{
|
|
TxError("\"%s\" isn't a valid %s option.\n", cmd->tx_argv[1],
|
|
cmd->tx_argv[0]);
|
|
option = LEF_HELP;
|
|
}
|
|
}
|
|
|
|
if (option != LEF_HELP)
|
|
{
|
|
selectedUse = CmdGetSelectedCell((Transform *)NULL);
|
|
if (selectedUse == NULL)
|
|
{
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
if (w == (MagWindow *) NULL)
|
|
{
|
|
if (ToolGetBox(&selectedDef,NULL) == FALSE)
|
|
{
|
|
TxError("Point to a window first\n");
|
|
return;
|
|
}
|
|
selectedUse = selectedDef->cd_parents;
|
|
}
|
|
else
|
|
selectedUse = (CellUse *)w->w_surfaceID;
|
|
}
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
case LEF_READ:
|
|
if (cmd->tx_argc > 3)
|
|
{
|
|
for (i = 3; i < cmd->tx_argc; i++)
|
|
{
|
|
if (*(cmd->tx_argv[i]) == '-')
|
|
{
|
|
if (!strncmp(cmd->tx_argv[i], "-import", 7))
|
|
lefImport = TRUE;
|
|
else if (!strncmp(cmd->tx_argv[i], "-anno", 5))
|
|
{
|
|
if (is_lef)
|
|
lefAnnotate = TRUE;
|
|
else
|
|
defAnnotate = TRUE;
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-label", 6))
|
|
{
|
|
if (is_lef)
|
|
TxPrintf("The \"-labels\" option is only for def read\n");
|
|
else
|
|
defLabelNets = TRUE;
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-noblock", 8))
|
|
{
|
|
if (is_lef)
|
|
TxPrintf("The \"-noblockage\" option is only for def read\n");
|
|
else
|
|
defNoBlockage = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (cmd->tx_argc < 3)
|
|
goto wrongNumArgs;
|
|
|
|
namep = cmd->tx_argv[2];
|
|
if (is_lef)
|
|
LefRead(namep, lefImport, lefAnnotate, lefDateStamp);
|
|
else
|
|
{
|
|
selectedDef = DefRead(namep, defLabelNets, defAnnotate, defNoBlockage);
|
|
if (selectedDef != (CellDef *)NULL)
|
|
{
|
|
/* Load this in the layout window */
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
if (w != (MagWindow *) NULL)
|
|
DBWloadWindow(w, selectedDef->cd_name, DBW_LOAD_IGNORE_TECH);
|
|
}
|
|
}
|
|
break;
|
|
case LEF_WRITEALL:
|
|
if (!is_lef)
|
|
{
|
|
TxError("Sorry, can't write hierarchical DEF at this time.\n");
|
|
TxError("Try \"def write\"\n");
|
|
}
|
|
else
|
|
{
|
|
for (i = 2; i < cmd->tx_argc; i++)
|
|
{
|
|
if (*(cmd->tx_argv[i]) == '-')
|
|
{
|
|
if (!strncmp(cmd->tx_argv[i], "-notop", 6))
|
|
lefTopCell = FALSE;
|
|
else if (!strncmp(cmd->tx_argv[i], "-tech", 5))
|
|
lefTech = TRUE;
|
|
else if (!strncmp(cmd->tx_argv[i], "-hide", 5))
|
|
{
|
|
lefHide = 0;
|
|
if ((i < (cmd->tx_argc - 1)) &&
|
|
StrIsNumeric(cmd->tx_argv[i + 1]))
|
|
{
|
|
lefHide = cmdParseCoord(w, cmd->tx_argv[i + 1],
|
|
FALSE, TRUE);
|
|
i++;
|
|
}
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-pinonly", 8))
|
|
{
|
|
lefPinOnly = 0;
|
|
if ((i < (cmd->tx_argc - 1)) &&
|
|
StrIsNumeric(cmd->tx_argv[i + 1]))
|
|
{
|
|
lefPinOnly = cmdParseCoord(w, cmd->tx_argv[i + 1],
|
|
FALSE, TRUE);
|
|
i++;
|
|
}
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-toplayer", 9))
|
|
lefTopLayer = TRUE;
|
|
else if (!strncmp(cmd->tx_argv[i], "-nomaster", 9))
|
|
lefDoMaster = FALSE;
|
|
else if (!strncmp(cmd->tx_argv[i], "-all", 4))
|
|
recurse = TRUE;
|
|
else goto wrongNumArgs;
|
|
}
|
|
else goto wrongNumArgs;
|
|
}
|
|
LefWriteAll(selectedUse, lefTopCell, lefTech, lefHide, lefPinOnly,
|
|
lefTopLayer, lefDoMaster, recurse);
|
|
}
|
|
break;
|
|
case LEF_WRITE:
|
|
allSpecial = FALSE;
|
|
cargs = cmd->tx_argc;
|
|
for (i = 2; i < cmd->tx_argc; i++)
|
|
{
|
|
if (*(cmd->tx_argv[i]) == '-')
|
|
{
|
|
if (!strncmp(cmd->tx_argv[i], "-allspec", 8))
|
|
{
|
|
if (!is_lef)
|
|
allSpecial = TRUE;
|
|
else
|
|
TxPrintf("The \"-allspec\" option is only for def write\n");
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-tech", 5))
|
|
{
|
|
if (is_lef)
|
|
lefTech = TRUE;
|
|
else
|
|
TxPrintf("The \"-tech\" option is only for lef write\n");
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-hide", 5))
|
|
{
|
|
if (is_lef)
|
|
{
|
|
lefHide = 0;
|
|
if ((i < (cmd->tx_argc - 1)) &&
|
|
StrIsNumeric(cmd->tx_argv[i + 1]))
|
|
{
|
|
lefHide = cmdParseCoord(w, cmd->tx_argv[i + 1],
|
|
FALSE, TRUE);
|
|
cargs--;
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
TxPrintf("The \"-hide\" option is only for lef write\n");
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-pinonly", 8))
|
|
{
|
|
if (is_lef)
|
|
{
|
|
lefPinOnly = 0;
|
|
if ((i < (cmd->tx_argc - 1)) &&
|
|
StrIsNumeric(cmd->tx_argv[i + 1]))
|
|
{
|
|
lefPinOnly = cmdParseCoord(w, cmd->tx_argv[i + 1],
|
|
FALSE, TRUE);
|
|
cargs--;
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
TxPrintf("The \"-pinonly\" option is only for lef write\n");
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-toplayer", 9))
|
|
{
|
|
if (is_lef)
|
|
lefTopLayer = TRUE;
|
|
else
|
|
TxPrintf("The \"-toplayer\" option is only for lef write\n");
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-nomaster", 9))
|
|
{
|
|
if (is_lef)
|
|
lefDoMaster = FALSE;
|
|
else
|
|
TxPrintf("The \"-nomaster\" option is only for lef write\n");
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-units", 6))
|
|
{
|
|
if (is_lef)
|
|
TxPrintf("The \"-units\" option is only for def write\n");
|
|
else
|
|
{
|
|
i++;
|
|
cargs--;
|
|
if ((cmd->tx_argc < i) || (!StrIsInt(cmd->tx_argv[i])))
|
|
{
|
|
TxPrintf("The \"-units\" option requires an argument.\n");
|
|
}
|
|
else
|
|
{
|
|
units = atoi(cmd->tx_argv[i]);
|
|
// To do: Check range of units
|
|
}
|
|
}
|
|
}
|
|
else if (!strncmp(cmd->tx_argv[i], "-anal", 5))
|
|
{
|
|
if (is_lef)
|
|
TxPrintf("The \"-anal\" option is only for def write\n");
|
|
else
|
|
defAnalRetentive = TRUE;
|
|
}
|
|
else goto wrongNumArgs;
|
|
cargs--;
|
|
}
|
|
else if (i != 2) /* Is argument a filename? */
|
|
goto wrongNumArgs;
|
|
}
|
|
if (cargs != 2 && cargs != 3) goto wrongNumArgs;
|
|
if (selectedUse == NULL)
|
|
{
|
|
TxError("No cell selected\n");
|
|
return;
|
|
}
|
|
if (cargs == 2)
|
|
namep = selectedUse->cu_def->cd_name;
|
|
else
|
|
namep = cmd->tx_argv[2];
|
|
if (!is_lef)
|
|
DefWriteCell(selectedUse->cu_def, namep, allSpecial, units,
|
|
defAnalRetentive);
|
|
else
|
|
LefWriteCell(selectedUse->cu_def, namep, selectedUse->cu_def
|
|
== EditRootDef, lefTech, lefHide, lefPinOnly,
|
|
lefTopLayer, lefDoMaster);
|
|
break;
|
|
case LEF_DATESTAMP:
|
|
if (!is_lef)
|
|
{
|
|
TxPrintf("The \"datestamp\" option is only for LEF reads.\n");
|
|
break;
|
|
}
|
|
if (cmd->tx_argc == 2)
|
|
{
|
|
#ifdef MAGIC_WRAPPER
|
|
if (lefDateStamp != -1)
|
|
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(lefDateStamp));
|
|
else
|
|
Tcl_SetObjResult(magicinterp, Tcl_NewStringObj("default", -1));
|
|
#else
|
|
if (lefDateStamp != -1)
|
|
TxPrintf("Macros will contain a header creation date "
|
|
"stamp of %d.\n", lefDateStamp);
|
|
else
|
|
TxPrintf("Macros will contain a default header creation date "
|
|
"stamp.\n");
|
|
#endif
|
|
return;
|
|
}
|
|
else if (cmd->tx_argc != 3)
|
|
goto wrongNumArgs;
|
|
|
|
if (!strcmp(cmd->tx_argv[2], "default"))
|
|
lefDateStamp = -1;
|
|
else if (StrIsInt(cmd->tx_argv[2]))
|
|
lefDateStamp = atoi(cmd->tx_argv[2]);
|
|
else
|
|
{
|
|
TxError("Unrecognizable date stamp \"%s\".\n", cmd->tx_argv[2]);
|
|
goto wrongNumArgs;
|
|
}
|
|
|
|
break;
|
|
case LEF_NOCHECK:
|
|
if (!is_lef)
|
|
{
|
|
TxPrintf("The \"nocheck\" option is only for LEF writes.\n");
|
|
break;
|
|
}
|
|
if (cmd->tx_argc == 2)
|
|
{
|
|
#ifdef MAGIC_WRAPPER
|
|
if (lefIgnoreNets == NULL)
|
|
Tcl_SetObjResult(magicinterp, Tcl_NewStringObj("none", -1));
|
|
else
|
|
{
|
|
Tcl_Obj *lobj;
|
|
linkedNetName *lnn;
|
|
|
|
lobj = Tcl_NewListObj(0, NULL);
|
|
for (lnn = lefIgnoreNets; lnn; lnn = lnn->lnn_next)
|
|
Tcl_ListObjAppendElement(magicinterp, lobj,
|
|
Tcl_NewStringObj(lnn->lnn_name, -1));
|
|
|
|
Tcl_SetObjResult(magicinterp, lobj);
|
|
}
|
|
#else
|
|
if (lefIgnoreNets == NULL)
|
|
TxPrintf("There are no net names being ignored.\n");
|
|
else
|
|
{
|
|
linkedNetName *lnn;
|
|
|
|
for (lnn = lefIgnoreNets; lnn; lnn = lnn->lnn_next)
|
|
TxPrintf("%s ", lnn->lnn_name);
|
|
|
|
TxPrintf("\n");
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
char *inet;
|
|
linkedNetName *lnn;
|
|
|
|
/* This is inefficient, but there should never be more than
|
|
* a few items in this list.
|
|
*/
|
|
for (i = 2; i < cmd->tx_argc; i++)
|
|
{
|
|
inet = cmd->tx_argv[i];
|
|
if (!strcasecmp(inet, "none"))
|
|
{
|
|
/* Remove all net names from the list */
|
|
for (lnn = lefIgnoreNets; lnn; lnn = lnn->lnn_next)
|
|
freeMagic(lnn);
|
|
lefIgnoreNets = NULL;
|
|
}
|
|
else
|
|
{
|
|
lnn = (linkedNetName *)mallocMagic(sizeof(linkedNetName));
|
|
lnn->lnn_name = StrDup((char **)NULL, inet);
|
|
lnn->lnn_next = lefIgnoreNets;
|
|
lefIgnoreNets = lnn;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LEF_HELP:
|
|
wrongNumArgs:
|
|
TxPrintf("The \"%s\" options are:\n", cmd->tx_argv[0]);
|
|
msg = (is_lef) ? &(cmdLefOption[0]) : &(cmdDefOption[0]);
|
|
for (; *msg != NULL; msg++)
|
|
{
|
|
TxPrintf(" %s %s\n", cmd->tx_argv[0], *msg);
|
|
}
|
|
break;
|
|
}
|
|
}
|