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; 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;
} }

View File

@ -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();

View File

@ -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;

View File

@ -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)
{ {

View File

@ -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);
@ -3196,6 +3225,70 @@ dbWriteCellFunc(cellUse, cdarg)
cellUse->cu_id); cellUse->cu_id);
} }
else else
{
#ifdef MAGIC_WRAPPER
char *tvar;
/* 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 (subbed == FALSE)
{
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 "~" */ /* If path starts with home path, then replace with "~" */
/* to make IP semi-portable between home directories */ /* to make IP semi-portable between home directories */
@ -3218,6 +3311,7 @@ dbWriteCellFunc(cellUse, cdarg)
cellUse->cu_id, pathstart); cellUse->cu_id, pathstart);
} }
} }
}
FPRINTR(arg->wa_file, cstring); FPRINTR(arg->wa_file, cstring);
cellUse->cu_def->cd_flags |= CDVISITED; cellUse->cu_def->cd_flags |= CDVISITED;