Modified the GDS read routine to give location information when

a self-intersecting or reversing path is seen in the input.
Added a new feature in which "save <cell>.tcl" will create a
file of magic commands that will re-create the cell when sourced
as a command file.
This commit is contained in:
R. Timothy Edwards 2025-09-05 17:51:46 -04:00
parent b1095b323c
commit 59a1953f3c
4 changed files with 369 additions and 5 deletions

View File

@ -1 +1 @@
8.3.547
8.3.548

View File

@ -439,7 +439,8 @@ CIFPaintWirePath(
/* Wire reverses direction. Break wire here, */
/* draw, and start new polygon. */
TxError("Warning: direction reversal in path.\n");
TxError("Warning: direction reversal in path at (%d, %d).\n",
pathp->cifp_x, pathp->cifp_y);
phi = theta;
if (endcap)
@ -451,7 +452,8 @@ CIFPaintWirePath(
firstpoint = TRUE;
}
else {
TxError("Error: mitre limit exceeded at wire junction.\n");
TxError("Error: mitre limit exceeded at wire junction at (%d, %d).\n",
pathp->cifp_x, pathp->cifp_y);
TxError("Route has been truncated.\n");
break;
}

View File

@ -220,8 +220,6 @@ CmdSave(
DBUpdateStamps(locDef);
if (cmd->tx_argc == 2)
{
char *fileName;
if (CmdIllegalChars(cmd->tx_argv[1], "[],", "Cell name"))
return;

View File

@ -3859,6 +3859,356 @@ dbWritePropFunc(key, value, cdata)
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* DBCellWriteCommandFile --
*
* Write out the contents of a cell to the specified file as a sequence
* of magic commands. Sourcing the resulting file will regenerate the
* cell.
*
* Results:
* TRUE if the cell could be written successfully, FALSE otherwise.
*
* Side effects:
* Writes a file to disk.
* Does NOT close the file 'f', but does fflush(f) before returning.
*
* ----------------------------------------------------------------------------
*/
bool
DBCellWriteCommandFile(cellDef, f)
CellDef *cellDef; /* Pointer to definition of cell to be written out */
FILE *f; /* The FILE to write to */
{
int dbWritePaintCommandsFunc();
int dbWriteUseCommandsFunc();
int dbWritePropCommandsFunc();
Label *lab;
struct writeArg arg;
int pNum;
TileType type, stype;
TileTypeBitMask typeMask, *sMask;
static const char *directionNames[] = {"c", "n", "ne", "e", "se",
"s", "sw", "w", "nw", 0};
if (f == NULL) return FALSE;
SigDisableInterrupts();
/* Write a descriptive header */
fprintf(f, "# Command script for generating cell %s\n", cellDef->cd_name);
fprintf(f, "\n");
fprintf(f, "suspendall\n");
fprintf(f, "tech unlock *\n");
fprintf(f, "snap internal\n");
fprintf(f, "load %s -silent\n", cellDef->cd_name);
fprintf(f, "box values 0 0 0 0\n");
/* These routines only need the file stream pointer */
arg.wa_name = NULL;
arg.wa_file = f;
arg.wa_reducer = 1;
for (type = TT_PAINTBASE; type < DBNumUserLayers; type++)
{
if ((pNum = DBPlane(type)) < 0)
continue;
arg.wa_found = FALSE;
arg.wa_type = type;
arg.wa_plane = pNum;
TTMaskSetOnlyType(&typeMask, type);
/* Add to the mask all generated (stacking) types which */
/* have this type as a residue. */
for (stype = DBNumUserLayers; stype < DBNumTypes; stype++)
{
sMask = DBResidueMask(stype);
if (TTMaskHasType(sMask, type))
TTMaskSetType(&typeMask, stype);
}
if (DBSrPaintArea((Tile *) NULL, cellDef->cd_planes[pNum],
&TiPlaneRect, &typeMask, dbWritePaintCommandsFunc, (ClientData) &arg))
goto ioerror;
}
if (DBCellEnum(cellDef, dbWriteUseCommandsFunc, (ClientData)&arg))
goto ioerror;
/* Now labels */
for (lab = cellDef->cd_labels; lab; lab = lab->lab_next)
{
if (strlen(lab->lab_text) == 0) continue; // Shouldn't happen
fprintf(f, "box values %d %d %d %d\n",
lab->lab_rect.r_xbot,
lab->lab_rect.r_ybot,
lab->lab_rect.r_xtop,
lab->lab_rect.r_ytop);
if (lab->lab_font < 0)
{
fprintf(f, "label %s %s %s\n",
lab->lab_text,
directionNames[lab->lab_just],
DBTypeLongName(lab->lab_type));
}
else
{
fprintf(f, "label %s %s %d %d %d %d %s %s\n",
lab->lab_text,
DBFontList[lab->lab_font]->mf_name,
lab->lab_size >> 3,
lab->lab_rotate,
lab->lab_offset.p_x,
lab->lab_offset.p_y,
directionNames[lab->lab_just],
DBTypeLongName(lab->lab_type));
}
if (lab->lab_flags & LABEL_STICKY)
{
fprintf(f, "select area label\n");
fprintf(f, "setlabel sticky true\n");
}
if (lab->lab_flags & PORT_DIR_MASK)
{
if (!(lab->lab_flags & LABEL_STICKY))
fprintf(f, "select area label\n");
fprintf(f, "port make %d\n", lab->lab_port);
if (lab->lab_flags & (PORT_DIR_NORTH | PORT_DIR_SOUTH | PORT_DIR_EAST
| PORT_DIR_WEST))
fprintf(f, "port connections");
if (lab->lab_flags & PORT_DIR_NORTH) fprintf(f, " n");
if (lab->lab_flags & PORT_DIR_SOUTH) fprintf(f, " s");
if (lab->lab_flags & PORT_DIR_EAST) fprintf(f, " e");
if (lab->lab_flags & PORT_DIR_WEST) fprintf(f, " w");
fprintf(f, "\n");
if (lab->lab_flags & PORT_USE_MASK)
{
fprintf(f, "port %d use ", lab->lab_port);
switch (lab->lab_flags & PORT_USE_MASK)
{
case PORT_USE_SIGNAL:
fprintf(f, "signal\n");
break;
case PORT_USE_ANALOG:
fprintf(f, "analog\n");
break;
case PORT_USE_POWER:
fprintf(f, "power\n");
break;
case PORT_USE_GROUND:
fprintf(f, "ground\n");
break;
case PORT_USE_CLOCK:
fprintf(f, "clock\n");
break;
}
}
if (lab->lab_flags & PORT_CLASS_MASK)
{
fprintf(f, "port %d class ", lab->lab_port);
switch (lab->lab_flags & PORT_CLASS_MASK)
{
case PORT_CLASS_INPUT:
fprintf(f, "input\n");
break;
case PORT_CLASS_OUTPUT:
fprintf(f, "output\n");
break;
case PORT_CLASS_TRISTATE:
fprintf(f, "tristate\n");
break;
case PORT_CLASS_BIDIRECTIONAL:
fprintf(f, "bidirectional\n");
break;
case PORT_CLASS_FEEDTHROUGH:
fprintf(f, "feedthrough\n");
break;
}
}
if (lab->lab_flags & PORT_SHAPE_MASK)
{
fprintf(f, "port %d shape ", lab->lab_port);
switch (lab->lab_flags & PORT_SHAPE_MASK)
{
case PORT_SHAPE_ABUT:
fprintf(f, "abutment\n");
break;
case PORT_SHAPE_RING:
fprintf(f, "ring\n");
break;
case PORT_SHAPE_THRU:
fprintf(f, "feedthrough\n");
break;
}
}
}
}
/* Note: Persistant elements should be handled (see dbwind/DBWelement.c) */
/* And any properties */
DBPropEnum(cellDef, dbWritePropCommandsFunc, (ClientData)&arg);
fprintf(f, "select clear\n");
fprintf(f, "view\n");
fprintf(f, "tech revert\n");
fprintf(f, "resumeall\n");
if (fflush(f) == EOF || ferror(f))
{
ioerror:
TxError("Warning: I/O error in writing file\n");
SigEnableInterrupts();
return (FALSE);
}
SigEnableInterrupts();
TxPrintf("Saved cell %s as a sequence of magic commands (file %s.tcl).\n",
cellDef->cd_name, cellDef->cd_name);
return (TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* dbWritePaintCommandsFunc ---
*
* Callback function used by DBCellWriteCommandFile() to output
* commands corresponding to cell layout geometry.
*
* ----------------------------------------------------------------------------
*/
int
dbWritePaintCommandsFunc(tile, cdarg)
Tile *tile;
ClientData cdarg;
{
char pstring[256];
struct writeArg *arg = (struct writeArg *) cdarg;
FILE *f = arg->wa_file;
TileType type = TiGetType(tile);
TileTypeBitMask *lMask, *rMask;
int diridx;
static const char *directionNames[] = {"nw", "se", "sw", "ne", 0};
/* This could be refined by merging metal areas across contacts,
* but the brute force procedure will do the job.
*/
if (IsSplit(tile))
{
diridx = (SplitDirection(tile) << 1) + SplitSide(tile);
fprintf(f, "box values %d %d %d %d\n",
LEFT(tile), BOTTOM(tile), RIGHT(tile), TOP(tile));
type = TiGetLeftType(tile);
if (type != TT_SPACE)
{
fprintf(f, "splitpaint %s %s\n", directionNames[diridx],
DBTypeLongNameTbl[type]);
}
type = TiGetRightType(tile);
if (type != TT_SPACE)
{
fprintf(f, "splitpaint %s %s\n", directionNames[diridx],
DBTypeLongNameTbl[type]);
}
}
else
{
type = TiGetType(tile);
fprintf(f, "box values %d %d %d %d\n",
LEFT(tile), BOTTOM(tile), RIGHT(tile), TOP(tile));
fprintf(f, "paint %s\n", DBTypeLongNameTbl[type]);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* dbWriteUseCommandsFunc ---
*
* Callback function used by DBCellWriteCommandFile() to output
* commands corresponding to cell uses in the layout.
*
* ----------------------------------------------------------------------------
*/
int
dbWriteUseCommandsFunc(cellUse, cdarg)
CellUse *cellUse;
ClientData cdarg;
{
struct writeArg *arg = (struct writeArg *) cdarg;
FILE *f = arg->wa_file;
fprintf(f, "box position %d %d\n", cellUse->cu_bbox.r_ll.p_x,
cellUse->cu_bbox.r_ll.p_y);
fprintf(f, "getcell %s\n", cellUse->cu_def->cd_name);
fprintf(f, "identify %s\n", cellUse->cu_id);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* dbWritePropCommandsFunc ---
*
* Callback function used by DBCellWriteCommandFile() to output
* commands corresponding to properties in the layout.
*
* ----------------------------------------------------------------------------
*/
int
dbWritePropCommandsFunc(key, value, cdarg)
char *key;
char *value;
ClientData cdarg;
{
struct writeArg *arg = (struct writeArg *) cdarg;
char *escstr, *p, *v;
int vallen;
FILE *f = arg->wa_file;
/* Probably need to escape more than just quotes here. */
vallen = strlen(value) + 1;
for (v = value; *v != '\0'; v++)
if (*v == '"') vallen++;
escstr = (char *)mallocMagic(vallen);
p = escstr;
for (v = value; *v != '\0'; v++)
{
if (*v == '"')
*p++ = '\\';
*p++ = *v;
}
*p = '\0';
fprintf(f, "property %s \"%s\"\n", key, escstr);
freeMagic(escstr);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
@ -3919,6 +4269,20 @@ DBCellWrite(cellDef, fileName)
result = FALSE;
/* Feature added 9/4/2025: If the filename ends with ".tcl",
* then write the cell as a series of magic commands, and don't
* otherwise alter the cell.
*/
if ((strlen(fileName) > 4) && (!strcmp(fileName + strlen(fileName) - 4, ".tcl")))
{
if ((realf = fopen(fileName, "w")))
{
result = DBCellWriteCommandFile(cellDef, realf);
fclose(realf);
return result;
}
}
/*
* Figure out the name of the file we will eventually write.
*/