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:
Tim Edwards 2020-12-04 16:56:51 -05:00
parent 1b3299ec90
commit 1c82265244
5 changed files with 165 additions and 23 deletions

View File

@ -290,6 +290,7 @@ calmaParseStructure(filename)
off_t filepos;
bool was_called;
bool was_initialized;
bool predefined;
CellDef *def;
/* 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 */
was_initialized = FALSE;
predefined = FALSE;
if (!calmaSkipExact(CALMA_BGNSTR)) goto syntaxerror;
if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
TxPrintf("Reading \"%s\".\n", strname);
@ -345,7 +347,12 @@ calmaParseStructure(filename)
freeMagic(newname);
}
}
cifReadCellDef = calmaFindCell(strname, &was_called);
cifReadCellDef = calmaFindCell(strname, &was_called, &predefined);
if (predefined == TRUE)
{
calmaNextCell();
return TRUE;
}
DBCellClearDef(cifReadCellDef);
DBCellSetAvail(cifReadCellDef);
HashSetValue(he, cifReadCellDef);
@ -666,7 +673,7 @@ calmaElementSref(filename)
{
TxPrintf("Cell definition %s does not exist!\n", sname);
fseek(calmaInputFile, originalFilePos, SEEK_SET);
def = calmaFindCell(sname, NULL);
def = calmaFindCell(sname, NULL, NULL);
/* Cell flags set to "dereferenced" in case there is no */
/* definition in the GDS file. If there is a definition */
/* 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))
{
@ -1031,13 +1038,16 @@ gdsCopyPaintFunc(tile, gdsCopyRec)
*/
CellDef *
calmaFindCell(name, was_called)
calmaFindCell(name, was_called, predefined)
char *name; /* Name of desired cell */
bool *was_called; /* If this cell is in the hash table, then it
* was instanced before it was defined. We
* need to know this so as to avoid flattening
* 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;
@ -1059,6 +1069,16 @@ calmaFindCell(name, was_called)
*/
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);
if (was_called) *was_called = FALSE;
}

View File

@ -77,6 +77,11 @@ bool CalmaPostOrder = FALSE; /* If TRUE, forces the GDS parser to
* flatten cells that are contact cuts.
* 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 int calmaWriteInitFunc();

View File

@ -31,6 +31,7 @@ extern bool CalmaDoLabels;
extern bool CalmaDoLibrary;
extern bool CalmaDoLower;
extern bool CalmaAddendum;
extern bool CalmaNoDuplicates;
extern bool CalmaMergeTiles;
extern bool CalmaFlattenArrays;
extern bool CalmaNoDRCCheck;

View File

@ -99,13 +99,14 @@ bool cmdDumpParseArgs();
#define CALMA_LIBRARY 8
#define CALMA_LOWER 9
#define CALMA_MERGE 10
#define CALMA_READ 11
#define CALMA_READONLY 12
#define CALMA_RESCALE 13
#define CALMA_WARNING 14
#define CALMA_WRITE 15
#define CALMA_POLYS 16
#define CALMA_PATHS 17
#define CALMA_NO_DUP 11
#define CALMA_READ 12
#define CALMA_READONLY 13
#define CALMA_RESCALE 14
#define CALMA_WARNING 15
#define CALMA_WRITE 16
#define CALMA_POLYS 17
#define CALMA_PATHS 18
#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",
"lower [yes|no] allow both upper and lower case in labels",
"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"
" into edit cell",
"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;
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:
if (cmd->tx_argc == 3)
{

View File

@ -45,6 +45,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#include <paths.h>
#endif
#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
@ -1373,7 +1374,7 @@ badTransform:
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.
*/
TxPrintf("Subcells are used circularly!\n");
@ -1382,6 +1383,33 @@ badTransform:
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 "/" */
/* or "~" and cellDef->cd_file has path components, then the path */
/* should be interpreted relative to the path of the parent cell. */
@ -3146,6 +3174,7 @@ dbWriteCellFunc(cellUse, cdarg)
struct writeArg *arg = (struct writeArg *) cdarg;
Transform *t;
Rect *b;
bool subbed = FALSE;
char cstring[256], *pathend, *pathstart, *parent;
t = &(cellUse->cu_transform);
@ -3197,25 +3226,90 @@ dbWriteCellFunc(cellUse, cdarg)
}
else
{
/* If path starts with home path, then replace with "~" */
/* to make IP semi-portable between home directories */
/* with the same file structure. */
#ifdef MAGIC_WRAPPER
char *tvar;
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))
&& (*(cellUse->cu_def->cd_file + strlen(homedir)) == '/'))
if (subbed == FALSE)
{
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_id, cellUse->cu_def->cd_file +
strlen(homedir));
}
else
{
sprintf(cstring, "use %s %c%s %s\n", cellUse->cu_def->cd_name,
}
else
{
sprintf(cstring, "use %s %c%s %s\n", cellUse->cu_def->cd_name,
(cellUse->cu_flags & CU_LOCKED) ? CULOCKCHAR : ' ',
cellUse->cu_id, pathstart);
}
}
}
FPRINTR(arg->wa_file, cstring);