Several enhancements: (1) Added command option "gds no_duplicates"
to allow a "gds read" command to ignore cells in the GDS which already exist in memory. This allows magic to be "pre-seeded" with specific views of cells in the GDS. Default is false, which is backwards- compatble behavior. (2) Changed the behavior of the the way the use path is written to and read from a .mag file, checking the path prefix against Tcl variables PDK_PATH, PDKPATH, PDK_ROOT, and PDKROOT, and replacing any such leading path component with the variable name. On reading a .mag file, any variable name at the start of the path that matches a Tcl variable will be substituted.
This commit is contained in:
parent
1b3299ec90
commit
1c82265244
|
|
@ -290,6 +290,7 @@ calmaParseStructure(filename)
|
||||||
off_t filepos;
|
off_t filepos;
|
||||||
bool was_called;
|
bool was_called;
|
||||||
bool was_initialized;
|
bool was_initialized;
|
||||||
|
bool predefined;
|
||||||
CellDef *def;
|
CellDef *def;
|
||||||
|
|
||||||
/* Make sure this is a structure; if not, let the caller know we're done */
|
/* Make sure this is a structure; if not, let the caller know we're done */
|
||||||
|
|
@ -299,6 +300,7 @@ calmaParseStructure(filename)
|
||||||
|
|
||||||
/* Read the structure name */
|
/* Read the structure name */
|
||||||
was_initialized = FALSE;
|
was_initialized = FALSE;
|
||||||
|
predefined = FALSE;
|
||||||
if (!calmaSkipExact(CALMA_BGNSTR)) goto syntaxerror;
|
if (!calmaSkipExact(CALMA_BGNSTR)) goto syntaxerror;
|
||||||
if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
|
if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
|
||||||
TxPrintf("Reading \"%s\".\n", strname);
|
TxPrintf("Reading \"%s\".\n", strname);
|
||||||
|
|
@ -345,7 +347,12 @@ calmaParseStructure(filename)
|
||||||
freeMagic(newname);
|
freeMagic(newname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cifReadCellDef = calmaFindCell(strname, &was_called);
|
cifReadCellDef = calmaFindCell(strname, &was_called, &predefined);
|
||||||
|
if (predefined == TRUE)
|
||||||
|
{
|
||||||
|
calmaNextCell();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
DBCellClearDef(cifReadCellDef);
|
DBCellClearDef(cifReadCellDef);
|
||||||
DBCellSetAvail(cifReadCellDef);
|
DBCellSetAvail(cifReadCellDef);
|
||||||
HashSetValue(he, cifReadCellDef);
|
HashSetValue(he, cifReadCellDef);
|
||||||
|
|
@ -666,7 +673,7 @@ calmaElementSref(filename)
|
||||||
{
|
{
|
||||||
TxPrintf("Cell definition %s does not exist!\n", sname);
|
TxPrintf("Cell definition %s does not exist!\n", sname);
|
||||||
fseek(calmaInputFile, originalFilePos, SEEK_SET);
|
fseek(calmaInputFile, originalFilePos, SEEK_SET);
|
||||||
def = calmaFindCell(sname, NULL);
|
def = calmaFindCell(sname, NULL, NULL);
|
||||||
/* Cell flags set to "dereferenced" in case there is no */
|
/* Cell flags set to "dereferenced" in case there is no */
|
||||||
/* definition in the GDS file. If there is a definition */
|
/* definition in the GDS file. If there is a definition */
|
||||||
/* made after the instance, then the flag will be cleared. */
|
/* made after the instance, then the flag will be cleared. */
|
||||||
|
|
@ -674,7 +681,7 @@ calmaElementSref(filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!def) def = calmaFindCell(sname, NULL);
|
if (!def) def = calmaFindCell(sname, NULL, NULL);
|
||||||
|
|
||||||
if (DBIsAncestor(def, cifReadCellDef))
|
if (DBIsAncestor(def, cifReadCellDef))
|
||||||
{
|
{
|
||||||
|
|
@ -1031,13 +1038,16 @@ gdsCopyPaintFunc(tile, gdsCopyRec)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CellDef *
|
CellDef *
|
||||||
calmaFindCell(name, was_called)
|
calmaFindCell(name, was_called, predefined)
|
||||||
char *name; /* Name of desired cell */
|
char *name; /* Name of desired cell */
|
||||||
bool *was_called; /* If this cell is in the hash table, then it
|
bool *was_called; /* If this cell is in the hash table, then it
|
||||||
* was instanced before it was defined. We
|
* was instanced before it was defined. We
|
||||||
* need to know this so as to avoid flattening
|
* need to know this so as to avoid flattening
|
||||||
* the cell if requested.
|
* the cell if requested.
|
||||||
*/
|
*/
|
||||||
|
bool *predefined; /* If this cell was in memory before the GDS
|
||||||
|
* file was read, then this flag gets set.
|
||||||
|
*/
|
||||||
|
|
||||||
{
|
{
|
||||||
HashEntry *h;
|
HashEntry *h;
|
||||||
|
|
@ -1059,6 +1069,16 @@ calmaFindCell(name, was_called)
|
||||||
*/
|
*/
|
||||||
DBReComputeBbox(def);
|
DBReComputeBbox(def);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TxPrintf("Warning: cell %s already existed before reading GDS!\n",
|
||||||
|
name);
|
||||||
|
if (CalmaNoDuplicates)
|
||||||
|
{
|
||||||
|
if (predefined) *predefined = TRUE;
|
||||||
|
TxPrintf("Using pre-existing cell definition\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
HashSetValue(h, def);
|
HashSetValue(h, def);
|
||||||
if (was_called) *was_called = FALSE;
|
if (was_called) *was_called = FALSE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,11 @@ bool CalmaPostOrder = FALSE; /* If TRUE, forces the GDS parser to
|
||||||
* flatten cells that are contact cuts.
|
* flatten cells that are contact cuts.
|
||||||
* Added by Nishit 8/16/2004
|
* Added by Nishit 8/16/2004
|
||||||
*/
|
*/
|
||||||
|
bool CalmaNoDuplicates = FALSE; /* If TRUE, then if a cell exists in
|
||||||
|
* memory with the same name as a cell
|
||||||
|
* in the GDS file, then the cell in
|
||||||
|
* the GDS file is skipped.
|
||||||
|
*/
|
||||||
extern void calmaUnexpected();
|
extern void calmaUnexpected();
|
||||||
extern int calmaWriteInitFunc();
|
extern int calmaWriteInitFunc();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ extern bool CalmaDoLabels;
|
||||||
extern bool CalmaDoLibrary;
|
extern bool CalmaDoLibrary;
|
||||||
extern bool CalmaDoLower;
|
extern bool CalmaDoLower;
|
||||||
extern bool CalmaAddendum;
|
extern bool CalmaAddendum;
|
||||||
|
extern bool CalmaNoDuplicates;
|
||||||
extern bool CalmaMergeTiles;
|
extern bool CalmaMergeTiles;
|
||||||
extern bool CalmaFlattenArrays;
|
extern bool CalmaFlattenArrays;
|
||||||
extern bool CalmaNoDRCCheck;
|
extern bool CalmaNoDRCCheck;
|
||||||
|
|
|
||||||
|
|
@ -99,13 +99,14 @@ bool cmdDumpParseArgs();
|
||||||
#define CALMA_LIBRARY 8
|
#define CALMA_LIBRARY 8
|
||||||
#define CALMA_LOWER 9
|
#define CALMA_LOWER 9
|
||||||
#define CALMA_MERGE 10
|
#define CALMA_MERGE 10
|
||||||
#define CALMA_READ 11
|
#define CALMA_NO_DUP 11
|
||||||
#define CALMA_READONLY 12
|
#define CALMA_READ 12
|
||||||
#define CALMA_RESCALE 13
|
#define CALMA_READONLY 13
|
||||||
#define CALMA_WARNING 14
|
#define CALMA_RESCALE 14
|
||||||
#define CALMA_WRITE 15
|
#define CALMA_WARNING 15
|
||||||
#define CALMA_POLYS 16
|
#define CALMA_WRITE 16
|
||||||
#define CALMA_PATHS 17
|
#define CALMA_POLYS 17
|
||||||
|
#define CALMA_PATHS 18
|
||||||
|
|
||||||
#define CALMA_WARN_HELP CIF_WARN_END /* undefined by CIF module */
|
#define CALMA_WARN_HELP CIF_WARN_END /* undefined by CIF module */
|
||||||
|
|
||||||
|
|
@ -138,6 +139,7 @@ CmdCalma(w, cmd)
|
||||||
"library [yes|no] do not output the top level, only subcells",
|
"library [yes|no] do not output the top level, only subcells",
|
||||||
"lower [yes|no] allow both upper and lower case in labels",
|
"lower [yes|no] allow both upper and lower case in labels",
|
||||||
"merge [yes|no] merge tiles into polygons in the output",
|
"merge [yes|no] merge tiles into polygons in the output",
|
||||||
|
"noduplicates [yes|no] do not read cells that exist before reading GDS",
|
||||||
"read file read Calma GDS-II format from \"file\"\n"
|
"read file read Calma GDS-II format from \"file\"\n"
|
||||||
" into edit cell",
|
" into edit cell",
|
||||||
"readonly [yes|no] set cell as read-only and generate output from GDS file",
|
"readonly [yes|no] set cell as read-only and generate output from GDS file",
|
||||||
|
|
@ -451,6 +453,26 @@ CmdCalma(w, cmd)
|
||||||
CalmaSubcellPolygons = (option < 3) ? FALSE : TRUE;
|
CalmaSubcellPolygons = (option < 3) ? FALSE : TRUE;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case CALMA_NO_DUP:
|
||||||
|
if (cmd->tx_argc == 2)
|
||||||
|
{
|
||||||
|
#ifdef MAGIC_WRAPPER
|
||||||
|
Tcl_SetObjResult(magicinterp, Tcl_NewBooleanObj(CalmaNoDuplicates));
|
||||||
|
#else
|
||||||
|
TxPrintf("Cell defs that exist before reading GDS will not be paresd.\n",
|
||||||
|
(CalmaNoDuplicates) ? "not " : "");
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cmd->tx_argc != 3)
|
||||||
|
goto wrongNumArgs;
|
||||||
|
|
||||||
|
option = Lookup(cmd->tx_argv[2], cmdCalmaYesNo);
|
||||||
|
if (option < 0)
|
||||||
|
goto wrongNumArgs;
|
||||||
|
CalmaNoDuplicates = (option < 3) ? FALSE : TRUE;
|
||||||
|
return;
|
||||||
|
|
||||||
case CALMA_PATHS:
|
case CALMA_PATHS:
|
||||||
if (cmd->tx_argc == 3)
|
if (cmd->tx_argc == 3)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
118
database/DBio.c
118
database/DBio.c
|
|
@ -45,6 +45,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "tcltk/tclmagic.h"
|
||||||
#include "utils/magic.h"
|
#include "utils/magic.h"
|
||||||
#include "utils/geometry.h"
|
#include "utils/geometry.h"
|
||||||
#include "tiles/tile.h"
|
#include "tiles/tile.h"
|
||||||
|
|
@ -1373,7 +1374,7 @@ badTransform:
|
||||||
else if (DBIsAncestor(subCellDef, cellDef))
|
else if (DBIsAncestor(subCellDef, cellDef))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Watchout for attempts to create circular structures.
|
* Watch out for attempts to create circular structures.
|
||||||
* If this happens, disregard the subcell.
|
* If this happens, disregard the subcell.
|
||||||
*/
|
*/
|
||||||
TxPrintf("Subcells are used circularly!\n");
|
TxPrintf("Subcells are used circularly!\n");
|
||||||
|
|
@ -1382,6 +1383,33 @@ badTransform:
|
||||||
goto nextLine;
|
goto nextLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAGIC_WRAPPER
|
||||||
|
/* If path starts with '$' then check for a possible Tcl variable */
|
||||||
|
/* replacement. */
|
||||||
|
|
||||||
|
if (*pathptr == '$')
|
||||||
|
{
|
||||||
|
char *varstart, *varend, savechar, *tvar;
|
||||||
|
|
||||||
|
varstart = pathptr + 1;
|
||||||
|
if (*varstart == '{') varstart++;
|
||||||
|
varend = varstart + 1;
|
||||||
|
while (*varend != '\0' && *varend != '}' && *varend != '/'
|
||||||
|
&& *varend != '\n' && *varend != ' ') varend++;
|
||||||
|
savechar = *varend;
|
||||||
|
*varend = '\0';
|
||||||
|
|
||||||
|
tvar = (char *)Tcl_GetVar(magicinterp, varstart, TCL_GLOBAL_ONLY);
|
||||||
|
*varend = savechar;
|
||||||
|
if (savechar == '}') varend++;
|
||||||
|
if (tvar)
|
||||||
|
{
|
||||||
|
memmove(pathptr + strlen(tvar), varend, strlen(varend) + 1);
|
||||||
|
memmove(pathptr, tvar, strlen(tvar));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Relative path handling: If path does not have a leading "/" */
|
/* Relative path handling: If path does not have a leading "/" */
|
||||||
/* or "~" and cellDef->cd_file has path components, then the path */
|
/* or "~" and cellDef->cd_file has path components, then the path */
|
||||||
/* should be interpreted relative to the path of the parent cell. */
|
/* should be interpreted relative to the path of the parent cell. */
|
||||||
|
|
@ -3146,6 +3174,7 @@ dbWriteCellFunc(cellUse, cdarg)
|
||||||
struct writeArg *arg = (struct writeArg *) cdarg;
|
struct writeArg *arg = (struct writeArg *) cdarg;
|
||||||
Transform *t;
|
Transform *t;
|
||||||
Rect *b;
|
Rect *b;
|
||||||
|
bool subbed = FALSE;
|
||||||
char cstring[256], *pathend, *pathstart, *parent;
|
char cstring[256], *pathend, *pathstart, *parent;
|
||||||
|
|
||||||
t = &(cellUse->cu_transform);
|
t = &(cellUse->cu_transform);
|
||||||
|
|
@ -3197,25 +3226,90 @@ dbWriteCellFunc(cellUse, cdarg)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* If path starts with home path, then replace with "~" */
|
#ifdef MAGIC_WRAPPER
|
||||||
/* to make IP semi-portable between home directories */
|
char *tvar;
|
||||||
/* with the same file structure. */
|
|
||||||
|
|
||||||
char *homedir = getenv("HOME");
|
/* Check for the leading component of the file path being equal to */
|
||||||
|
/* one of several common variable names for the PDK location, and */
|
||||||
|
/* if there is a match, then substitute the variable name for the */
|
||||||
|
/* matching leading path component. */
|
||||||
|
|
||||||
if (!strncmp(cellUse->cu_def->cd_file, homedir, strlen(homedir))
|
if (subbed == FALSE)
|
||||||
&& (*(cellUse->cu_def->cd_file + strlen(homedir)) == '/'))
|
|
||||||
{
|
{
|
||||||
sprintf(cstring, "use %s %c%s ~%s\n", cellUse->cu_def->cd_name,
|
tvar = (char *)Tcl_GetVar(magicinterp, "PDK_PATH", TCL_GLOBAL_ONLY);
|
||||||
|
if (tvar)
|
||||||
|
if (!strncmp(pathstart, tvar, strlen(tvar)))
|
||||||
|
{
|
||||||
|
sprintf(cstring, "use %s %c%s $PDK_PATH%s\n",
|
||||||
|
cellUse->cu_def->cd_name,
|
||||||
|
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
||||||
|
cellUse->cu_id, pathstart + strlen(tvar));
|
||||||
|
subbed = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subbed == FALSE)
|
||||||
|
{
|
||||||
|
tvar = (char *)Tcl_GetVar(magicinterp, "PDKPATH", TCL_GLOBAL_ONLY);
|
||||||
|
if (tvar)
|
||||||
|
if (!strncmp(pathstart, tvar, strlen(tvar)))
|
||||||
|
{
|
||||||
|
sprintf(cstring, "use %s %c%s $PDKPATH%s\n",
|
||||||
|
cellUse->cu_def->cd_name,
|
||||||
|
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
||||||
|
cellUse->cu_id, pathstart + strlen(tvar));
|
||||||
|
subbed = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subbed == FALSE)
|
||||||
|
{
|
||||||
|
tvar = (char *)Tcl_GetVar(magicinterp, "PDK_ROOT", TCL_GLOBAL_ONLY);
|
||||||
|
if (tvar)
|
||||||
|
if (!strncmp(pathstart, tvar, strlen(tvar)))
|
||||||
|
{
|
||||||
|
sprintf(cstring, "use %s %c%s $PDK_ROOT%s\n",
|
||||||
|
cellUse->cu_def->cd_name,
|
||||||
|
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
||||||
|
cellUse->cu_id, pathstart + strlen(tvar));
|
||||||
|
subbed = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subbed == FALSE)
|
||||||
|
{
|
||||||
|
tvar = (char *)Tcl_GetVar(magicinterp, "PDKROOT", TCL_GLOBAL_ONLY);
|
||||||
|
if (tvar)
|
||||||
|
if (!strncmp(pathstart, tvar, strlen(tvar)))
|
||||||
|
{
|
||||||
|
sprintf(cstring, "use %s %c%s $PDKROOT%s\n",
|
||||||
|
cellUse->cu_def->cd_name,
|
||||||
|
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
||||||
|
cellUse->cu_id, pathstart + strlen(tvar));
|
||||||
|
subbed = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (subbed == FALSE)
|
||||||
|
{
|
||||||
|
/* If path starts with home path, then replace with "~" */
|
||||||
|
/* to make IP semi-portable between home directories */
|
||||||
|
/* with the same file structure. */
|
||||||
|
|
||||||
|
char *homedir = getenv("HOME");
|
||||||
|
|
||||||
|
if (!strncmp(cellUse->cu_def->cd_file, homedir, strlen(homedir))
|
||||||
|
&& (*(cellUse->cu_def->cd_file + strlen(homedir)) == '/'))
|
||||||
|
{
|
||||||
|
sprintf(cstring, "use %s %c%s ~%s\n", cellUse->cu_def->cd_name,
|
||||||
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
||||||
cellUse->cu_id, cellUse->cu_def->cd_file +
|
cellUse->cu_id, cellUse->cu_def->cd_file +
|
||||||
strlen(homedir));
|
strlen(homedir));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprintf(cstring, "use %s %c%s %s\n", cellUse->cu_def->cd_name,
|
sprintf(cstring, "use %s %c%s %s\n", cellUse->cu_def->cd_name,
|
||||||
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
|
||||||
cellUse->cu_id, pathstart);
|
cellUse->cu_id, pathstart);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FPRINTR(arg->wa_file, cstring);
|
FPRINTR(arg->wa_file, cstring);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue