2017-04-25 14:41:48 +02:00
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* defRead.c --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* This module incorporates the LEF/DEF format for standard-cell place and
|
|
|
|
|
* route.
|
|
|
|
|
*
|
|
|
|
|
* Version 0.1 (September 26, 2003): DEF input of designs.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
2025-01-04 10:01:41 +01:00
|
|
|
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/lef/defRead.c,v 1.2 2008/06/01 18:37:43 tim Exp $";
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <math.h> /* for roundf() function, if std=c99 */
|
|
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "utils/undo.h"
|
2020-03-05 20:29:54 +01:00
|
|
|
#include "utils/utils.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "graphics/graphics.h"
|
|
|
|
|
#include "utils/main.h"
|
|
|
|
|
#include "cif/cif.h"
|
|
|
|
|
#include "lef/lefInt.h"
|
|
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "commands/commands.h"
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefAddRoutes --
|
|
|
|
|
*
|
|
|
|
|
* Parse a network route statement from the DEF file,
|
|
|
|
|
* and add it to the linked list representing the route.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns the last token encountered.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Reads from input stream;
|
|
|
|
|
* Adds information to the layout database.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
enum def_netspecial_keys {DEF_SPECNET_SHAPE = 0, DEF_SPECNET_STYLE,
|
|
|
|
|
DEF_SPECNET_USE, DEF_SPECNET_VOLTAGE, DEF_SPECNET_FIXEDBUMP,
|
|
|
|
|
DEF_SPECNET_ORIGINAL, DEF_SPECNET_PATTERN, DEF_SPECNET_ESTCAP,
|
2023-08-25 18:15:58 +02:00
|
|
|
DEF_SPECNET_WEIGHT, DEF_SPECNET_PROPERTY, DEF_SPECNET_ROUTED,
|
|
|
|
|
DEF_SPECNET_FIXED, DEF_SPECNET_COVER, DEF_SPECNET_SHIELD };
|
2019-07-03 19:58:13 +02:00
|
|
|
|
|
|
|
|
enum def_netspecial_shape_keys {
|
|
|
|
|
DEF_SPECNET_SHAPE_RING = 0,
|
|
|
|
|
DEF_SPECNET_SHAPE_PADRING,
|
|
|
|
|
DEF_SPECNET_SHAPE_BLOCKRING,
|
|
|
|
|
DEF_SPECNET_SHAPE_STRIPE,
|
|
|
|
|
DEF_SPECNET_SHAPE_FOLLOWPIN,
|
|
|
|
|
DEF_SPECNET_SHAPE_IOWIRE,
|
|
|
|
|
DEF_SPECNET_SHAPE_COREWIRE,
|
|
|
|
|
DEF_SPECNET_SHAPE_BLOCKWIRE,
|
|
|
|
|
DEF_SPECNET_SHAPE_BLOCKAGEWIRE,
|
|
|
|
|
DEF_SPECNET_SHAPE_FILLWIRE,
|
|
|
|
|
DEF_SPECNET_SHAPE_FILLWIREOPC,
|
|
|
|
|
DEF_SPECNET_SHAPE_DRCFILL};
|
|
|
|
|
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *
|
2025-01-04 09:50:40 +01:00
|
|
|
DefAddRoutes(
|
|
|
|
|
CellDef *rootDef, /* Cell to paint */
|
|
|
|
|
FILE *f, /* Input file */
|
|
|
|
|
float oscale, /* Scale factor between LEF and magic units */
|
|
|
|
|
bool special, /* True if this section is SPECIALNETS */
|
|
|
|
|
char *netname, /* Name of the net, if net is to be labeled */
|
|
|
|
|
LefRules *ruleset, /* Non-default rule, or NULL */
|
|
|
|
|
LefMapping *defLayerMap, /* magic-to-lef layer mapping array */
|
|
|
|
|
bool annotate) /* If TRUE, do not generate any geometry */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
LinkedRect *routeList, *newRoute = NULL, *routeTop = NULL;
|
|
|
|
|
Point refp; /* reference point */
|
|
|
|
|
bool valid = FALSE; /* is there a valid reference point? */
|
|
|
|
|
bool initial = TRUE;
|
2020-03-05 20:29:54 +01:00
|
|
|
bool labeled = TRUE;
|
2024-03-21 03:15:41 +01:00
|
|
|
bool iscontact = FALSE;
|
2022-03-30 23:17:09 +02:00
|
|
|
Rect locarea, r;
|
2019-07-03 20:52:22 +02:00
|
|
|
int extend, lextend, hextend;
|
2017-04-25 14:41:48 +02:00
|
|
|
float x, y, z, w;
|
2022-11-09 17:15:06 +01:00
|
|
|
int paintWidth, saveWidth, paintExtend;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType routeLayer, paintLayer;
|
|
|
|
|
HashEntry *he;
|
2022-03-23 15:49:02 +01:00
|
|
|
lefLayer *lefl = NULL;
|
|
|
|
|
lefRule *rule = NULL;
|
2019-07-03 19:58:13 +02:00
|
|
|
int keyword;
|
2022-03-23 15:49:02 +01:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const specnet_keys[] = {
|
2019-07-03 19:58:13 +02:00
|
|
|
"SHAPE",
|
|
|
|
|
"STYLE",
|
|
|
|
|
"USE",
|
|
|
|
|
"VOLTAGE",
|
|
|
|
|
"FIXEDBUMP",
|
|
|
|
|
"ORIGINAL",
|
|
|
|
|
"PATTERN",
|
|
|
|
|
"ESTCAP",
|
|
|
|
|
"WEIGHT",
|
|
|
|
|
"PROPERTY",
|
2023-08-25 18:15:58 +02:00
|
|
|
"ROUTED",
|
|
|
|
|
"FIXED",
|
|
|
|
|
"COVER",
|
|
|
|
|
"SHIELD",
|
2019-07-03 19:58:13 +02:00
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const specnet_shape_keys[] = {
|
2019-07-03 19:58:13 +02:00
|
|
|
"RING",
|
|
|
|
|
"PADRING",
|
|
|
|
|
"BLOCKRING",
|
|
|
|
|
"STRIPE",
|
|
|
|
|
"FOLLOWPIN",
|
|
|
|
|
"IOWIRE",
|
|
|
|
|
"COREWIRE",
|
|
|
|
|
"BLOCKWIRE",
|
|
|
|
|
"BLOCKAGEWIRE",
|
|
|
|
|
"FILLWIRE",
|
|
|
|
|
"FILLWIREOPC",
|
|
|
|
|
"DRCFILL",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-05 20:29:54 +01:00
|
|
|
if (netname != NULL) labeled = FALSE;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
while (initial || (token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Get next point, token "NEW", or via name */
|
|
|
|
|
if (initial || !strcmp(token, "NEW") || !strcmp(token, "new"))
|
|
|
|
|
{
|
|
|
|
|
/* initial pass is like a NEW record, but has no NEW keyword */
|
|
|
|
|
initial = FALSE;
|
|
|
|
|
|
|
|
|
|
/* invalidate reference point */
|
|
|
|
|
valid = FALSE;
|
|
|
|
|
|
2024-03-21 03:15:41 +01:00
|
|
|
/* assume this is not a via unless found otherwise */
|
|
|
|
|
iscontact = FALSE;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&LefInfo, token);
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
lefl = (lefLayer *)HashGetValue(he);
|
|
|
|
|
if (lefl) routeLayer = lefl->type;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* The fallback position is to match the DEF name to the */
|
|
|
|
|
/* magic layer name. This is ad-hoc, and the declaration */
|
|
|
|
|
/* of type mappings in the lef section of the techfile is */
|
|
|
|
|
/* preferred. */
|
|
|
|
|
|
2025-02-18 14:45:36 +01:00
|
|
|
routeLayer = LefHelper_DBTechNameType_LefLower(token);
|
2017-04-25 14:41:48 +02:00
|
|
|
lefl = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (routeLayer < 0)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Unknown layer type \"%s\" for NEW route\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
paintLayer = routeLayer;
|
|
|
|
|
|
|
|
|
|
if (special)
|
|
|
|
|
{
|
|
|
|
|
/* SPECIALNETS has the additional width */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%f", &w) != 1)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Bad width in special net\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (w != 0)
|
|
|
|
|
paintWidth = (int)roundf(w / oscale);
|
|
|
|
|
else
|
|
|
|
|
paintWidth = (lefl) ? lefl->info.route.width :
|
|
|
|
|
DEFAULT_WIDTH * DBLambda[1] / DBLambda[0];
|
2022-11-09 17:15:06 +01:00
|
|
|
paintExtend = 0; /* SPECIALNETS always have 0 wire extension */
|
2017-04-25 14:41:48 +02:00
|
|
|
saveWidth = paintWidth;
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-03-23 22:08:39 +01:00
|
|
|
{
|
|
|
|
|
if (ruleset)
|
2022-11-18 02:24:39 +01:00
|
|
|
{
|
2022-03-23 22:08:39 +01:00
|
|
|
for (rule = ruleset->rule; rule; rule = rule->next)
|
|
|
|
|
if (rule->lefInfo == lefl)
|
|
|
|
|
break;
|
2022-11-18 02:24:39 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
rule = NULL;
|
2022-03-23 22:08:39 +01:00
|
|
|
|
|
|
|
|
paintWidth = (rule) ? rule->width :
|
|
|
|
|
(lefl) ? lefl->info.route.width :
|
2017-04-25 14:41:48 +02:00
|
|
|
DEFAULT_WIDTH * DBLambda[1] / DBLambda[0];
|
2023-09-27 03:26:31 +02:00
|
|
|
paintExtend = (rule) ? rule->width : paintWidth;
|
2022-03-23 22:08:39 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-07-03 19:58:13 +02:00
|
|
|
else if ((*token == '+') && (special == TRUE))
|
|
|
|
|
{
|
|
|
|
|
int netstyle;
|
|
|
|
|
|
|
|
|
|
/* Check for SHAPE, STYLE, or USE keywords */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, specnet_keys);
|
2019-07-03 19:58:13 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in SPECIALNET "
|
|
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch(keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_SPECNET_STYLE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &netstyle) != 1)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_INFO, "Net style \"%s\" in SPECIALNET "
|
|
|
|
|
"definition is not a number; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_SPECNET_SHAPE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, specnet_shape_keys);
|
2019-07-03 19:58:13 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_INFO, "Unknown SHAPE \"%s\" in SPECIALNET "
|
|
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_SPECNET_PROPERTY:
|
|
|
|
|
/* Ignore except to absorb the next two tokens. */
|
|
|
|
|
token = LefNextToken(f, TRUE); /* Drop through */
|
|
|
|
|
|
|
|
|
|
case DEF_SPECNET_USE:
|
|
|
|
|
case DEF_SPECNET_VOLTAGE:
|
|
|
|
|
case DEF_SPECNET_ORIGINAL:
|
|
|
|
|
case DEF_SPECNET_PATTERN:
|
|
|
|
|
case DEF_SPECNET_ESTCAP:
|
|
|
|
|
case DEF_SPECNET_WEIGHT:
|
|
|
|
|
/* Ignore except to absorb the next token. */
|
|
|
|
|
token = LefNextToken(f, TRUE); /* Drop through */
|
|
|
|
|
|
|
|
|
|
case DEF_SPECNET_FIXEDBUMP:
|
|
|
|
|
/* Ignore this keyword */
|
|
|
|
|
break;
|
2023-08-25 18:15:58 +02:00
|
|
|
|
|
|
|
|
case DEF_SPECNET_SHIELD:
|
|
|
|
|
/* Treat as the following case except to absorb the next token */
|
|
|
|
|
token = LefNextToken(f, TRUE); /* Drop through */
|
|
|
|
|
|
|
|
|
|
case DEF_SPECNET_ROUTED:
|
|
|
|
|
case DEF_SPECNET_COVER:
|
|
|
|
|
case DEF_SPECNET_FIXED:
|
|
|
|
|
initial = TRUE;
|
|
|
|
|
continue;
|
2019-07-03 19:58:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "RECT"))
|
|
|
|
|
{
|
|
|
|
|
/* NOTE: Use of "RECT" in NETS is not in the LEF/DEF spec. */
|
|
|
|
|
/* However, its use has been seen. So "special" is not */
|
|
|
|
|
/* checked here. */
|
|
|
|
|
|
2019-07-03 21:36:21 +02:00
|
|
|
/* The rectangle coordinates are relative to the current */
|
|
|
|
|
/* reference point, not absolute. */
|
|
|
|
|
|
|
|
|
|
newRoute = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
|
|
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
/* Read an (llx lly urx ury) rectangle */
|
|
|
|
|
token = LefNextToken(f, TRUE); /* read llx */
|
|
|
|
|
if (*token == '(') token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%f", &x) == 1)
|
|
|
|
|
{
|
2019-07-03 21:36:21 +02:00
|
|
|
locarea.r_xbot = (refp.p_x / 2) + (int)roundf(x / oscale);
|
2019-07-03 19:58:13 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Cannot parse X coordinate in RECT.\n");
|
2019-07-03 19:58:13 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE); /* read lly */
|
|
|
|
|
if (sscanf(token, "%f", &y) == 1)
|
|
|
|
|
{
|
2019-07-03 21:36:21 +02:00
|
|
|
locarea.r_ybot = (refp.p_y / 2) + (int)roundf(y / oscale);
|
2019-07-03 19:58:13 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Cannot parse Y coordinate in RECT.\n");
|
2019-07-03 19:58:13 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE); /* read urx */
|
|
|
|
|
if (sscanf(token, "%f", &x) == 1)
|
|
|
|
|
{
|
2019-07-03 21:36:21 +02:00
|
|
|
locarea.r_xtop = (refp.p_x / 2) + (int)roundf(x / oscale);
|
2019-07-03 19:58:13 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Cannot parse X coordinate in RECT.\n");
|
2019-07-03 19:58:13 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE); /* read ury */
|
|
|
|
|
if (sscanf(token, "%f", &y) == 1)
|
|
|
|
|
{
|
2019-07-03 21:36:21 +02:00
|
|
|
locarea.r_ytop = (refp.p_y / 2) + (int)roundf(y / oscale);
|
2019-07-03 19:58:13 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Cannot parse Y coordinate in RECT.\n");
|
2019-07-03 19:58:13 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE); /* read closing parens */
|
|
|
|
|
if (*token != ')')
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Bad coordinates in RECT.\n");
|
2019-07-03 19:58:13 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
2019-07-03 21:36:21 +02:00
|
|
|
GeoCanonicalRect(&locarea, &newRoute->r_r);
|
2019-07-03 19:58:13 +02:00
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "POLYGON"))
|
|
|
|
|
{
|
2025-01-06 17:25:38 +01:00
|
|
|
LefError(DEF_ERROR, "Route has %s entries, this is not handled!\n",
|
2019-07-03 19:58:13 +02:00
|
|
|
token);
|
|
|
|
|
token = LefNextToken(f, TRUE); /* read opening parens */
|
|
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "VIRTUAL"))
|
|
|
|
|
{
|
|
|
|
|
/* Is this a LEF 5.8 thing? Not sure if it should be ignored! */
|
|
|
|
|
/* Should the whole wire leg be ignored? */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-03-23 15:49:02 +01:00
|
|
|
else if (!strcmp(token, "TAPER"))
|
|
|
|
|
{
|
|
|
|
|
/* Return to the default width for this layer */
|
|
|
|
|
paintWidth = (lefl) ? lefl->info.route.width :
|
|
|
|
|
DEFAULT_WIDTH * DBLambda[1] / DBLambda[0];
|
2022-11-09 17:15:06 +01:00
|
|
|
paintExtend = (special) ? 0 : paintWidth;
|
2022-03-23 15:49:02 +01:00
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "TAPERRULE"))
|
|
|
|
|
{
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&LefNonDefaultRules, token);
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
2022-03-23 22:08:39 +01:00
|
|
|
LefRules *tempruleset = (LefRules *)HashGetValue(he);
|
|
|
|
|
for (rule = tempruleset->rule; rule; rule = rule->next)
|
|
|
|
|
if (rule->lefInfo == lefl)
|
|
|
|
|
break;
|
|
|
|
|
|
2022-11-09 17:15:06 +01:00
|
|
|
if (rule)
|
|
|
|
|
{
|
|
|
|
|
paintWidth = rule->width;
|
2023-09-27 03:26:31 +02:00
|
|
|
paintExtend = rule->width;
|
2022-11-09 17:15:06 +01:00
|
|
|
}
|
2022-03-23 22:08:39 +01:00
|
|
|
}
|
|
|
|
|
else if (!strcmp(token, "DEFAULT"))
|
|
|
|
|
{
|
|
|
|
|
paintWidth = (lefl) ? lefl->info.route.width :
|
|
|
|
|
DEFAULT_WIDTH * DBLambda[1] / DBLambda[0];
|
2022-11-09 17:15:06 +01:00
|
|
|
paintExtend = (special) ? 0 : paintWidth;
|
2022-03-23 15:49:02 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
LefError(DEF_ERROR, "Unknown nondefault rule \"%s\"\n", token);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else if (*token != '(') /* via name */
|
|
|
|
|
{
|
|
|
|
|
/* A '+' or ';' record ends the route */
|
|
|
|
|
if (*token == ';' || *token == '+')
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
else if (valid == FALSE)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Route has via name \"%s\" but no points!\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
he = HashLookOnly(&LefInfo, token);
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
lefl = (lefLayer *)HashGetValue(he);
|
|
|
|
|
newRoute = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
|
|
|
|
|
|
|
|
|
|
/* The area to paint is derived from the via definitions. */
|
|
|
|
|
|
|
|
|
|
if (lefl != NULL)
|
|
|
|
|
{
|
|
|
|
|
LinkedRect *viaRoute, *addRoute;
|
|
|
|
|
|
|
|
|
|
/* If there is a LinkedRect structure for the via, */
|
|
|
|
|
/* add those records to the route first. */
|
|
|
|
|
|
|
|
|
|
for (viaRoute = lefl->info.via.lr; viaRoute != NULL;
|
|
|
|
|
viaRoute = viaRoute->r_next)
|
|
|
|
|
{
|
|
|
|
|
addRoute = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
|
|
|
|
|
addRoute->r_next = NULL;
|
|
|
|
|
addRoute->r_type = viaRoute->r_type;
|
|
|
|
|
addRoute->r_r = viaRoute->r_r;
|
|
|
|
|
|
|
|
|
|
addRoute->r_r.r_xbot += refp.p_x;
|
|
|
|
|
addRoute->r_r.r_ybot += refp.p_y;
|
|
|
|
|
addRoute->r_r.r_xtop += refp.p_x;
|
|
|
|
|
addRoute->r_r.r_ytop += refp.p_y;
|
|
|
|
|
|
|
|
|
|
addRoute->r_r.r_xbot >>= 1;
|
|
|
|
|
addRoute->r_r.r_ybot >>= 1;
|
|
|
|
|
addRoute->r_r.r_xtop >>= 1;
|
|
|
|
|
addRoute->r_r.r_ytop >>= 1;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (routeTop)
|
|
|
|
|
routeList->r_next = addRoute;
|
|
|
|
|
else
|
|
|
|
|
routeTop = addRoute;
|
|
|
|
|
|
|
|
|
|
routeList = addRoute;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
paintLayer = lefl->type;
|
|
|
|
|
|
|
|
|
|
newRoute->r_r.r_xbot = refp.p_x + lefl->info.via.area.r_xbot;
|
|
|
|
|
newRoute->r_r.r_ybot = refp.p_y + lefl->info.via.area.r_ybot;
|
|
|
|
|
newRoute->r_r.r_xtop = refp.p_x + lefl->info.via.area.r_xtop;
|
|
|
|
|
newRoute->r_r.r_ytop = refp.p_y + lefl->info.via.area.r_ytop;
|
|
|
|
|
|
|
|
|
|
newRoute->r_r.r_xbot >>= 1;
|
|
|
|
|
newRoute->r_r.r_ybot >>= 1;
|
|
|
|
|
newRoute->r_r.r_xtop >>= 1;
|
|
|
|
|
newRoute->r_r.r_ytop >>= 1;
|
|
|
|
|
|
2024-03-21 03:15:41 +01:00
|
|
|
iscontact = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2025-02-18 14:45:36 +01:00
|
|
|
else if ((paintLayer = LefHelper_DBTechNameType_LefLower(token)) >= 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Error: Via \"%s\" named but undefined.\n",
|
|
|
|
|
token);
|
2017-04-25 14:41:48 +02:00
|
|
|
newRoute->r_r.r_xbot = refp.p_x - paintWidth;
|
|
|
|
|
newRoute->r_r.r_ybot = refp.p_y - paintWidth;
|
|
|
|
|
newRoute->r_r.r_xtop = refp.p_x + paintWidth;
|
|
|
|
|
newRoute->r_r.r_ytop = refp.p_y + paintWidth;
|
|
|
|
|
|
|
|
|
|
newRoute->r_r.r_xbot >>= 1;
|
|
|
|
|
newRoute->r_r.r_ybot >>= 1;
|
|
|
|
|
newRoute->r_r.r_xtop >>= 1;
|
|
|
|
|
newRoute->r_r.r_ytop >>= 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Via name \"%s\" unknown in route.\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* After the via, the new route layer becomes whatever */
|
|
|
|
|
/* residue of the via was NOT the previous route layer. */
|
|
|
|
|
/* This is absolutely impossible to make consistent */
|
|
|
|
|
/* with the DEF spec, but there you have it. . . */
|
|
|
|
|
|
2024-03-21 03:15:41 +01:00
|
|
|
if (iscontact || (DBIsContact(paintLayer)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TileTypeBitMask *rMask = DBResidueMask(paintLayer);
|
|
|
|
|
TileType stype;
|
|
|
|
|
|
|
|
|
|
for (stype = TT_TECHDEPBASE; stype < DBNumUserLayers; stype++)
|
|
|
|
|
if (TTMaskHasType(rMask, stype))
|
|
|
|
|
if (stype != routeLayer)
|
|
|
|
|
{
|
|
|
|
|
/* Diagnostic */
|
|
|
|
|
/*
|
|
|
|
|
TxPrintf("Contact %s: In=%s Out=%s\n",
|
|
|
|
|
DBTypeLongNameTbl[paintLayer],
|
|
|
|
|
DBTypeLongNameTbl[routeLayer],
|
|
|
|
|
DBTypeLongNameTbl[stype]);
|
|
|
|
|
*/
|
|
|
|
|
routeLayer = stype;
|
2024-03-20 22:19:45 +01:00
|
|
|
|
2025-02-26 22:53:21 +01:00
|
|
|
ASSERT(routeLayer >= 0, "routeLayer<0"); /* positive bounds check */
|
2017-04-25 14:41:48 +02:00
|
|
|
lefl = defLayerMap[routeLayer].lefInfo;
|
2024-03-20 22:19:45 +01:00
|
|
|
|
|
|
|
|
/* Get correct rule for nondefault rules */
|
|
|
|
|
if (ruleset)
|
|
|
|
|
{
|
|
|
|
|
for (rule = ruleset->rule; rule; rule = rule->next)
|
|
|
|
|
if (rule->lefInfo == lefl)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
rule = NULL;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (special)
|
|
|
|
|
paintWidth = saveWidth;
|
|
|
|
|
else
|
2024-03-20 22:19:45 +01:00
|
|
|
paintWidth = (rule) ? rule->width :
|
|
|
|
|
(lefl) ? lefl->info.route.width :
|
|
|
|
|
DEFAULT_WIDTH * DBLambda[1] / DBLambda[0];
|
|
|
|
|
|
2022-11-09 17:15:06 +01:00
|
|
|
paintExtend = (special) ? 0 : paintWidth;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2024-03-21 03:15:41 +01:00
|
|
|
iscontact = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Via name \"%s\" unknown in route.\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Revert to the routing layer type, in case we painted a via */
|
|
|
|
|
paintLayer = routeLayer;
|
|
|
|
|
|
|
|
|
|
/* Record current reference point */
|
|
|
|
|
locarea.r_xbot = refp.p_x;
|
|
|
|
|
locarea.r_ybot = refp.p_y;
|
2019-07-03 20:52:22 +02:00
|
|
|
lextend = extend;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
/* Read an (X Y [extend]) point */
|
2017-04-25 14:41:48 +02:00
|
|
|
token = LefNextToken(f, TRUE); /* read X */
|
|
|
|
|
if (*token == '*')
|
|
|
|
|
{
|
|
|
|
|
if (valid == FALSE)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (sscanf(token, "%f", &x) == 1)
|
|
|
|
|
{
|
|
|
|
|
refp.p_x = (int)roundf((2 * x) / oscale);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Cannot parse X coordinate.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE); /* read Y */
|
|
|
|
|
if (*token == '*')
|
|
|
|
|
{
|
|
|
|
|
if (valid == FALSE)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n");
|
2022-03-26 02:38:41 +01:00
|
|
|
if (newRoute)
|
|
|
|
|
{
|
|
|
|
|
freeMagic(newRoute);
|
|
|
|
|
newRoute = NULL;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (sscanf(token, "%f", &y) == 1)
|
|
|
|
|
{
|
|
|
|
|
refp.p_y = (int)roundf((2 * y) / oscale);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
LefError(DEF_ERROR, "Cannot parse Y coordinate.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
goto endCoord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Extension is half-width for regular nets, 0 for special nets */
|
|
|
|
|
/* 0 for special nets is *not* how the 5.3 spec reads, but it */
|
|
|
|
|
/* is apparently how everyone interprets it, and is true for */
|
|
|
|
|
/* 5.6 spec. */
|
|
|
|
|
|
2022-11-09 17:15:06 +01:00
|
|
|
extend = paintExtend;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (*token != ')')
|
|
|
|
|
{
|
|
|
|
|
/* non-default route extension */
|
|
|
|
|
if (sscanf(token, "%f", &z) != 1)
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Can't parse route extension value.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* all values will be divided by 2, so we need */
|
|
|
|
|
/* to multiply up by 2 now. */
|
|
|
|
|
|
|
|
|
|
else
|
2019-07-03 19:58:13 +02:00
|
|
|
extend = (int)roundf((2 * z) / oscale);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Indicate that we have a valid reference point */
|
|
|
|
|
|
|
|
|
|
if (valid == FALSE)
|
|
|
|
|
{
|
|
|
|
|
valid = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if ((locarea.r_xbot != refp.p_x) && (locarea.r_ybot != refp.p_y))
|
|
|
|
|
{
|
|
|
|
|
/* Skip over nonmanhattan segments, reset the reference */
|
|
|
|
|
/* point, and output a warning. */
|
|
|
|
|
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Can't deal with nonmanhattan geometry in route.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
locarea.r_xbot = refp.p_x;
|
|
|
|
|
locarea.r_ybot = refp.p_y;
|
2019-07-03 20:52:22 +02:00
|
|
|
lextend = extend;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newRoute = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
|
|
|
|
|
|
|
|
|
|
/* Route coordinates become the centerline of the */
|
|
|
|
|
/* segment. "refp" is kept in 1/2 lambda units so */
|
|
|
|
|
/* we should always end up with integer units. */
|
|
|
|
|
|
|
|
|
|
locarea.r_xtop = refp.p_x;
|
|
|
|
|
locarea.r_ytop = refp.p_y;
|
|
|
|
|
|
2019-07-03 20:52:22 +02:00
|
|
|
/* Change route segment to a canonical rectangle. If */
|
|
|
|
|
/* the route is flipped relative to canonical coords, */
|
|
|
|
|
/* then the wire extentions have to be swapped as well. */
|
|
|
|
|
|
|
|
|
|
if ((locarea.r_xtop < locarea.r_xbot) ||
|
|
|
|
|
(locarea.r_ytop < locarea.r_ybot))
|
|
|
|
|
{
|
|
|
|
|
hextend = lextend;
|
|
|
|
|
lextend = extend;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
hextend = extend;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
GeoCanonicalRect(&locarea, &newRoute->r_r);
|
|
|
|
|
|
|
|
|
|
if (newRoute->r_r.r_xbot == newRoute->r_r.r_xtop)
|
|
|
|
|
{
|
|
|
|
|
newRoute->r_r.r_xbot -= paintWidth;
|
|
|
|
|
newRoute->r_r.r_xtop += paintWidth;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-07-03 20:52:22 +02:00
|
|
|
newRoute->r_r.r_xbot -= lextend;
|
|
|
|
|
newRoute->r_r.r_xtop += hextend;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newRoute->r_r.r_ybot == newRoute->r_r.r_ytop)
|
|
|
|
|
{
|
|
|
|
|
newRoute->r_r.r_ybot -= paintWidth;
|
|
|
|
|
newRoute->r_r.r_ytop += paintWidth;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-07-03 20:52:22 +02:00
|
|
|
newRoute->r_r.r_ybot -= lextend;
|
|
|
|
|
newRoute->r_r.r_ytop += hextend;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we don't have integer units here, we should */
|
|
|
|
|
/* rescale the magic internal grid. */
|
|
|
|
|
|
|
|
|
|
newRoute->r_r.r_xbot >>= 1;
|
|
|
|
|
newRoute->r_r.r_ybot >>= 1;
|
|
|
|
|
newRoute->r_r.r_xtop >>= 1;
|
|
|
|
|
newRoute->r_r.r_ytop >>= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
endCoord:
|
|
|
|
|
/* Find the closing parenthesis for the coordinate pair */
|
|
|
|
|
while (*token != ')')
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Link in the new route segment */
|
|
|
|
|
if (newRoute)
|
|
|
|
|
{
|
|
|
|
|
newRoute->r_type = paintLayer;
|
|
|
|
|
newRoute->r_next = NULL;
|
|
|
|
|
|
|
|
|
|
if (routeTop)
|
|
|
|
|
routeList->r_next = newRoute;
|
|
|
|
|
else
|
|
|
|
|
routeTop = newRoute;
|
|
|
|
|
|
|
|
|
|
routeList = newRoute;
|
|
|
|
|
newRoute = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Process each segment and paint into the layout */
|
|
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
while (routeTop != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* paint */
|
2022-03-30 19:02:12 +02:00
|
|
|
if (annotate == FALSE)
|
2022-03-30 23:17:09 +02:00
|
|
|
{
|
2022-03-30 19:02:12 +02:00
|
|
|
DBPaint(rootDef, &routeTop->r_r, routeTop->r_type);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-03-30 23:17:09 +02:00
|
|
|
/* label */
|
|
|
|
|
if (labeled == FALSE)
|
|
|
|
|
{
|
|
|
|
|
r.r_xbot = r.r_xtop = (routeTop->r_r.r_xbot + routeTop->r_r.r_xtop) / 2;
|
|
|
|
|
r.r_ybot = r.r_ytop = (routeTop->r_r.r_ybot + routeTop->r_r.r_ytop) / 2;
|
|
|
|
|
DBPutLabel(rootDef, &r, GEO_CENTER, netname, routeTop->r_type, 0, 0);
|
|
|
|
|
labeled = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2020-03-05 20:29:54 +01:00
|
|
|
{
|
2022-03-30 23:17:09 +02:00
|
|
|
/* When annotating, make sure there is a valid layer under the */
|
|
|
|
|
/* label. If not, then wait for the next bit of geometry. */
|
|
|
|
|
|
|
|
|
|
if (labeled == FALSE)
|
|
|
|
|
{
|
|
|
|
|
Tile *tp;
|
|
|
|
|
Plane *plane = rootDef->cd_planes[DBPlane(routeTop->r_type)];
|
2025-02-21 19:15:02 +01:00
|
|
|
tp = PlaneGetHint(plane);
|
2022-03-30 23:17:09 +02:00
|
|
|
GOTOPOINT(tp, &routeTop->r_r.r_ll);
|
|
|
|
|
if (TiGetType(tp) == routeTop->r_type)
|
|
|
|
|
{
|
|
|
|
|
r.r_xbot = r.r_xtop =
|
|
|
|
|
(routeTop->r_r.r_xbot + routeTop->r_r.r_xtop) / 2;
|
|
|
|
|
r.r_ybot = r.r_ytop =
|
|
|
|
|
(routeTop->r_r.r_ybot + routeTop->r_r.r_ytop) / 2;
|
|
|
|
|
DBPutLabel(rootDef, &r, GEO_CENTER, netname, routeTop->r_type,
|
|
|
|
|
0, 0);
|
|
|
|
|
labeled = TRUE;
|
|
|
|
|
}
|
|
|
|
|
if ((labeled == FALSE) && (routeTop->r_next == NULL))
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: Label \"%s\" did not land on any existing"
|
|
|
|
|
" net geometry.\n", netname);
|
|
|
|
|
r.r_xbot = r.r_xtop =
|
|
|
|
|
(routeTop->r_r.r_xbot + routeTop->r_r.r_xtop) / 2;
|
|
|
|
|
r.r_ybot = r.r_ytop =
|
|
|
|
|
(routeTop->r_r.r_ybot + routeTop->r_r.r_ytop) / 2;
|
|
|
|
|
DBPutLabel(rootDef, &r, GEO_CENTER, netname, routeTop->r_type,
|
|
|
|
|
0, 0);
|
|
|
|
|
labeled = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-05 20:29:54 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* advance to next point and free record (1-delayed) */
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, (char *)routeTop);
|
2017-04-25 14:41:48 +02:00
|
|
|
routeTop = routeTop->r_next;
|
|
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
return token; /* Pass back the last token found */
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 15:49:02 +01:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefReadNonDefaultRules --
|
|
|
|
|
*
|
|
|
|
|
* Read a NONDEFAULTRULES section from a DEF file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Adds information into the non-default rules hash table.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum def_nondef_keys {DEF_NONDEF_START = 0, DEF_NONDEF_END};
|
|
|
|
|
enum def_nondefprop_keys {
|
|
|
|
|
DEF_NONDEFPROP_HARDSPACING = 0, DEF_NONDEFPROP_LAYER,
|
|
|
|
|
DEF_NONDEFPROP_VIA, DEF_NONDEFPROP_VIARULE,
|
|
|
|
|
DEF_NONDEFPROP_MINCUTS, DEF_NONDEFPROP_PROPERTY,
|
|
|
|
|
DEF_NONDEFLAYER_WIDTH, DEF_NONDEFLAYER_DIAG,
|
2022-03-23 22:08:39 +01:00
|
|
|
DEF_NONDEFLAYER_SPACE, DEF_NONDEFLAYER_EXT,
|
|
|
|
|
DEF_NONDEFPROP_DONE};
|
2022-03-23 15:49:02 +01:00
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
DefReadNonDefaultRules(
|
|
|
|
|
FILE *f,
|
|
|
|
|
CellDef *rootDef,
|
|
|
|
|
const char *sname,
|
|
|
|
|
float oscale,
|
|
|
|
|
int total)
|
2022-03-23 15:49:02 +01:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2022-03-23 15:49:02 +01:00
|
|
|
int keyword, subkey;
|
|
|
|
|
int processed = 0;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
lefLayer *lefl;
|
|
|
|
|
float fvalue;
|
2022-03-23 22:08:39 +01:00
|
|
|
LefRules *ruleset = NULL;
|
2022-03-23 15:49:02 +01:00
|
|
|
lefRule *rule = NULL;
|
2022-03-23 22:08:39 +01:00
|
|
|
bool inlayer;
|
2022-03-23 15:49:02 +01:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const nondef_keys[] = {
|
2022-03-23 15:49:02 +01:00
|
|
|
"-",
|
|
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const nondef_property_keys[] = {
|
2022-03-23 15:49:02 +01:00
|
|
|
"HARDSPACING",
|
|
|
|
|
"LAYER",
|
|
|
|
|
"VIA",
|
|
|
|
|
"VIARULE",
|
|
|
|
|
"MINCUTS",
|
|
|
|
|
"PROPERTY",
|
|
|
|
|
"WIDTH",
|
|
|
|
|
"DIAGWIDTH",
|
|
|
|
|
"SPACING",
|
|
|
|
|
"WIREEXT",
|
2022-03-23 22:08:39 +01:00
|
|
|
";",
|
2022-03-23 15:49:02 +01:00
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, nondef_keys);
|
2022-03-23 15:49:02 +01:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in NONDEFAULTRULES "
|
|
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_NONDEF_START:
|
|
|
|
|
|
|
|
|
|
/* Get non-default rule name */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
|
|
|
|
|
/* Create a hash entry for this nondefault rule */
|
|
|
|
|
/* NOTE: Needs to handle name collisions. */
|
2022-03-23 22:08:39 +01:00
|
|
|
he = HashFind(&LefNonDefaultRules, token);
|
|
|
|
|
ruleset = (LefRules *)mallocMagic(sizeof(LefRules));
|
|
|
|
|
HashSetValue(he, ruleset);
|
|
|
|
|
ruleset->name = StrDup((char **)NULL, token);
|
|
|
|
|
ruleset->rule = NULL;
|
|
|
|
|
processed++;
|
2022-03-23 15:49:02 +01:00
|
|
|
|
|
|
|
|
/* Process all properties */
|
|
|
|
|
while (token && (*token != ';'))
|
|
|
|
|
{
|
|
|
|
|
if (*token != '+')
|
|
|
|
|
{
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2022-03-23 22:08:39 +01:00
|
|
|
if (!inlayer)
|
|
|
|
|
continue;
|
2022-03-23 15:49:02 +01:00
|
|
|
}
|
2022-11-18 02:24:39 +01:00
|
|
|
if (*token == '+')
|
2022-03-23 22:08:39 +01:00
|
|
|
{
|
|
|
|
|
inlayer = FALSE;
|
|
|
|
|
rule = NULL;
|
2022-03-23 15:49:02 +01:00
|
|
|
token = LefNextToken(f, TRUE);
|
2022-03-23 22:08:39 +01:00
|
|
|
}
|
2022-03-23 15:49:02 +01:00
|
|
|
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, nondef_property_keys);
|
2022-03-23 15:49:02 +01:00
|
|
|
if (subkey < 0)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_INFO, "Unknown non-default rule property \"%s\" "
|
|
|
|
|
"in NONDEFAULTRULE definition; ignoring.\n", token);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (subkey)
|
|
|
|
|
{
|
2022-03-23 22:08:39 +01:00
|
|
|
case DEF_NONDEFPROP_DONE:
|
|
|
|
|
break;
|
2022-03-23 15:49:02 +01:00
|
|
|
case DEF_NONDEFPROP_HARDSPACING:
|
|
|
|
|
lefl = NULL;
|
|
|
|
|
/* Ignore this */
|
|
|
|
|
break;
|
|
|
|
|
case DEF_NONDEFPROP_VIA:
|
|
|
|
|
case DEF_NONDEFPROP_VIARULE:
|
|
|
|
|
lefl = NULL;
|
|
|
|
|
/* Fall through */
|
|
|
|
|
case DEF_NONDEFPROP_LAYER:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
he = HashFind(&LefInfo, token);
|
|
|
|
|
lefl = (lefLayer *)HashGetValue(he);
|
2022-03-23 22:08:39 +01:00
|
|
|
if (ruleset)
|
|
|
|
|
{
|
|
|
|
|
/* Chain new layer rule to linked list */
|
|
|
|
|
rule = (lefRule *)mallocMagic(sizeof(lefRule));
|
2022-03-23 15:49:02 +01:00
|
|
|
rule->lefInfo = lefl;
|
2022-03-23 22:08:39 +01:00
|
|
|
rule->width = 0;
|
|
|
|
|
rule->spacing = 0;
|
2023-09-27 03:26:31 +02:00
|
|
|
rule->extend = 0; /* unused */
|
2022-03-23 22:08:39 +01:00
|
|
|
rule->next = ruleset->rule;
|
|
|
|
|
ruleset->rule = rule;
|
|
|
|
|
}
|
2022-03-23 15:49:02 +01:00
|
|
|
else
|
|
|
|
|
LefError(DEF_INFO, "No non-default rule name for \"%s\" "
|
|
|
|
|
"in NONDEFAULTRULE definition!.\n", token);
|
2022-03-31 16:23:39 +02:00
|
|
|
if (subkey == DEF_NONDEFPROP_LAYER) inlayer = TRUE;
|
2022-03-23 15:49:02 +01:00
|
|
|
break;
|
|
|
|
|
case DEF_NONDEFPROP_MINCUTS:
|
|
|
|
|
case DEF_NONDEFPROP_PROPERTY:
|
|
|
|
|
lefl = NULL;
|
|
|
|
|
/* Ignore the next two tokens */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_NONDEFLAYER_WIDTH:
|
2022-03-23 22:08:39 +01:00
|
|
|
if (!inlayer)
|
|
|
|
|
LefError(DEF_INFO, "WIDTH specified without layer.\n");
|
2022-03-23 15:49:02 +01:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
sscanf(token, "%f", &fvalue);
|
2022-03-23 22:08:39 +01:00
|
|
|
if (rule == NULL)
|
|
|
|
|
LefError(DEF_INFO, "No rule for non-default width.\n");
|
|
|
|
|
else if (lefl == NULL)
|
2022-03-23 15:49:02 +01:00
|
|
|
LefError(DEF_INFO, "No layer for non-default width.\n");
|
2022-03-23 22:08:39 +01:00
|
|
|
else
|
|
|
|
|
rule->width = (int)roundf(fvalue / oscale);
|
2022-03-23 15:49:02 +01:00
|
|
|
break;
|
|
|
|
|
case DEF_NONDEFLAYER_SPACE:
|
2022-03-23 22:08:39 +01:00
|
|
|
if (!inlayer)
|
|
|
|
|
LefError(DEF_INFO, "SPACING specified without layer.\n");
|
2022-03-23 15:49:02 +01:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
sscanf(token, "%f", &fvalue);
|
2022-03-23 22:08:39 +01:00
|
|
|
if (rule == NULL)
|
|
|
|
|
LefError(DEF_INFO, "No rule for non-default spacing.\n");
|
|
|
|
|
else if (lefl == NULL)
|
|
|
|
|
LefError(DEF_INFO, "No layer for non-default spacing.\n");
|
|
|
|
|
else
|
|
|
|
|
rule->spacing = (int)roundf(fvalue / oscale);
|
2022-03-23 15:49:02 +01:00
|
|
|
break;
|
|
|
|
|
case DEF_NONDEFLAYER_EXT:
|
2022-11-09 17:15:06 +01:00
|
|
|
if (!inlayer)
|
|
|
|
|
LefError(DEF_INFO, "WIREEXT specified without layer.\n");
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
sscanf(token, "%f", &fvalue);
|
|
|
|
|
if (rule == NULL)
|
|
|
|
|
LefError(DEF_INFO, "No rule for non-default extension.\n");
|
|
|
|
|
else if (lefl == NULL)
|
|
|
|
|
LefError(DEF_INFO, "No layer for non-default extension.\n");
|
|
|
|
|
else
|
2023-09-27 03:26:31 +02:00
|
|
|
{
|
|
|
|
|
LefError(DEF_WARNING, "Non-default extension at via not implemented.\n");
|
2022-11-09 17:15:06 +01:00
|
|
|
rule->extend = (int)roundf((2 * fvalue) / oscale);
|
2023-09-27 03:26:31 +02:00
|
|
|
}
|
2022-11-09 17:15:06 +01:00
|
|
|
break;
|
|
|
|
|
case DEF_NONDEFLAYER_DIAG:
|
2022-03-23 22:08:39 +01:00
|
|
|
if (!inlayer)
|
|
|
|
|
LefError(DEF_INFO,
|
|
|
|
|
"Layer value specified without layer.\n");
|
2022-03-23 15:49:02 +01:00
|
|
|
/* Absorb token and ignore */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-23 21:02:22 +02:00
|
|
|
inlayer = FALSE;
|
2022-03-23 15:49:02 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_NONDEF_END:
|
|
|
|
|
if (!LefParseEndStatement(f, sname))
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Non-default rule END statement missing.\n");
|
|
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == DEF_NONDEF_END) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processed == total)
|
|
|
|
|
TxPrintf(" Processed %d non-default rules total.\n", processed);
|
|
|
|
|
else
|
|
|
|
|
LefError(DEF_WARNING, "Number of non-default rules read (%d) does not match "
|
|
|
|
|
"the number declared (%d).\n", processed, total);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-01 02:02:12 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* defFoundOneFunc --
|
|
|
|
|
*
|
|
|
|
|
* Simple callback function for DefReadNets() when using
|
|
|
|
|
* the "def read -annoatate" option. Attempts to find
|
|
|
|
|
* paint in the top level in the area of a pin that is
|
|
|
|
|
* part of the net.
|
|
|
|
|
*
|
|
|
|
|
* Note: The routine does not search on connected tiles
|
|
|
|
|
* and so could miss the connecting material, although
|
|
|
|
|
* there are multiple fall-back chances to succeed.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* 1 to stop the search, as we just take the first tile
|
|
|
|
|
* found and run with it.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Copies a pointer to the tile found into the client
|
|
|
|
|
* data record.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-04 09:50:40 +01:00
|
|
|
defFoundOneFunc(
|
|
|
|
|
Tile *tile,
|
2026-01-03 02:12:37 +01:00
|
|
|
TileType dinfo, /* (unused) */
|
2025-01-04 09:50:40 +01:00
|
|
|
Tile **tret)
|
2022-04-01 02:02:12 +02:00
|
|
|
{
|
|
|
|
|
*tret = tile;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefReadNets --
|
|
|
|
|
*
|
|
|
|
|
* Read a NETS or SPECIALNETS section from a DEF file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Many. Networks are created, and geometry may be
|
|
|
|
|
* painted into the database top-level cell.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum def_net_keys {DEF_NET_START = 0, DEF_NET_END};
|
|
|
|
|
enum def_netprop_keys {
|
2022-03-23 22:08:39 +01:00
|
|
|
DEF_NETPROP_USE = 0, DEF_NETPROP_ROUTED, DEF_NETPROP_NOSHIELD,
|
|
|
|
|
DEF_NETPROP_FIXED, DEF_NETPROP_COVER, DEF_NETPROP_SOURCE,
|
|
|
|
|
DEF_NETPROP_SHIELDNET, DEF_NETPROP_SUBNET, DEF_NETPROP_VPIN,
|
|
|
|
|
DEF_NETPROP_XTALK, DEF_NETPROP_NONDEFRULE, DEF_NETPROP_FIXEDBUMP,
|
|
|
|
|
DEF_NETPROP_FREQUENCY, DEF_NETPROP_ORIGINAL, DEF_NETPROP_PATTERN,
|
|
|
|
|
DEF_NETPROP_ESTCAP, DEF_NETPROP_WEIGHT, DEF_NETPROP_PROPERTY
|
|
|
|
|
};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
DefReadNets(
|
|
|
|
|
FILE *f,
|
|
|
|
|
CellDef *rootDef,
|
|
|
|
|
const char *sname,
|
|
|
|
|
float oscale,
|
|
|
|
|
bool special, /* True if this section is SPECIALNETS */
|
|
|
|
|
bool dolabels, /* If true, create a label for each net */
|
|
|
|
|
bool annotate, /* If true, create labels, not geometry */
|
|
|
|
|
int total)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2022-04-01 02:02:12 +02:00
|
|
|
char *netname = NULL, *prnet;
|
2017-04-25 14:41:48 +02:00
|
|
|
int keyword, subkey;
|
|
|
|
|
int processed = 0;
|
|
|
|
|
LefMapping *defLayerMap;
|
2022-03-23 22:08:39 +01:00
|
|
|
LefRules *ruleset = NULL;
|
|
|
|
|
HashEntry *he;
|
2022-04-01 02:02:12 +02:00
|
|
|
bool needanno;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const net_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"-",
|
|
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const net_property_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"USE",
|
|
|
|
|
"ROUTED",
|
2022-03-23 22:08:39 +01:00
|
|
|
"NOSHIELD",
|
2017-04-25 14:41:48 +02:00
|
|
|
"FIXED",
|
|
|
|
|
"COVER",
|
|
|
|
|
"SOURCE",
|
2022-03-23 22:08:39 +01:00
|
|
|
"SHIELDNET",
|
|
|
|
|
"SUBNET",
|
|
|
|
|
"VPIN",
|
|
|
|
|
"XTALK",
|
|
|
|
|
"NONDEFAULTRULE",
|
|
|
|
|
"FIXEDBUMP",
|
|
|
|
|
"FREQUENCY",
|
|
|
|
|
"ORIGINAL",
|
|
|
|
|
"PATTERN",
|
|
|
|
|
"ESTCAP",
|
2017-04-25 14:41:48 +02:00
|
|
|
"WEIGHT",
|
|
|
|
|
"PROPERTY",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-04 03:37:32 +01:00
|
|
|
defLayerMap = defMakeInverseLayerMap(LAYER_MAP_VIAS);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, net_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in NET "
|
2017-04-25 14:41:48 +02:00
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_NET_START:
|
|
|
|
|
|
|
|
|
|
/* Get net name */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2020-03-05 20:29:54 +01:00
|
|
|
if (dolabels) netname = StrDup((char **)NULL, token);
|
2022-04-01 02:02:12 +02:00
|
|
|
needanno = annotate;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Update the record of the number of nets processed */
|
|
|
|
|
/* and spit out a message for every 5% finished. */
|
|
|
|
|
|
|
|
|
|
LefEstimate(processed++, total,
|
|
|
|
|
(special) ? "special nets" : "nets");
|
|
|
|
|
|
|
|
|
|
/* Process all properties */
|
|
|
|
|
while (token && (*token != ';'))
|
|
|
|
|
{
|
2022-04-01 02:02:12 +02:00
|
|
|
if (needanno)
|
|
|
|
|
{
|
|
|
|
|
char *compname, *termname;
|
|
|
|
|
|
|
|
|
|
/* Annotation only---when back-annotating */
|
|
|
|
|
/* labels into a layout, the safest place to */
|
|
|
|
|
/* put labels is on a terminal position */
|
|
|
|
|
|
|
|
|
|
if (*token == '(')
|
|
|
|
|
{
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (!strcmp(token, "PIN"))
|
|
|
|
|
needanno = FALSE;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Rect r;
|
|
|
|
|
bool isvalid;
|
|
|
|
|
TileType ttype;
|
|
|
|
|
Tile *tp;
|
|
|
|
|
|
|
|
|
|
compname = StrDup((char **)NULL, token);
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
termname = (char *)mallocMagic(strlen(compname) +
|
|
|
|
|
strlen(token) + 3);
|
|
|
|
|
sprintf(termname, "%s/%s", compname, token);
|
|
|
|
|
ttype = CmdFindNetProc(termname, EditCellUse, &r,
|
|
|
|
|
FALSE, &isvalid);
|
|
|
|
|
if (isvalid)
|
|
|
|
|
{
|
|
|
|
|
/* The pin was found. However, there may not be
|
|
|
|
|
* paint on the top level over the whole pin.
|
|
|
|
|
* Search the area (+1) for attached paint, then
|
|
|
|
|
* label inside that tile.
|
|
|
|
|
*/
|
|
|
|
|
tp = NULL;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL,
|
|
|
|
|
rootDef->cd_planes[DBPlane(ttype)],
|
|
|
|
|
&r, &DBConnectTbl[ttype],
|
|
|
|
|
defFoundOneFunc, (ClientData)&tp);
|
|
|
|
|
|
|
|
|
|
if (tp != NULL)
|
|
|
|
|
{
|
|
|
|
|
TiToRect(tp, &r);
|
|
|
|
|
r.r_xbot = r.r_xtop = (r.r_xbot + r.r_xtop) / 2;
|
|
|
|
|
r.r_ybot = r.r_ytop = (r.r_ybot + r.r_ytop) / 2;
|
|
|
|
|
DBPutLabel(rootDef, &r, GEO_CENTER, netname,
|
|
|
|
|
ttype, 0, 0);
|
|
|
|
|
needanno = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
freeMagic(termname);
|
|
|
|
|
freeMagic(compname);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* All connections are ignored, and we go */
|
|
|
|
|
/* go directly to the first property ("+") key */
|
|
|
|
|
|
|
|
|
|
if (*token != '+')
|
|
|
|
|
{
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, net_property_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subkey < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown net property \"%s\" in "
|
2017-04-25 14:41:48 +02:00
|
|
|
"NET definition; ignoring.\n", token);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (subkey)
|
|
|
|
|
{
|
|
|
|
|
case DEF_NETPROP_ROUTED:
|
|
|
|
|
case DEF_NETPROP_FIXED:
|
|
|
|
|
case DEF_NETPROP_COVER:
|
2022-03-23 22:08:39 +01:00
|
|
|
case DEF_NETPROP_NOSHIELD:
|
2022-04-01 02:02:12 +02:00
|
|
|
prnet = NULL;
|
|
|
|
|
if (dolabels && (needanno || (!annotate)))
|
|
|
|
|
prnet = netname;
|
2017-04-25 14:41:48 +02:00
|
|
|
token = DefAddRoutes(rootDef, f, oscale, special,
|
2022-04-01 02:02:12 +02:00
|
|
|
prnet, ruleset, defLayerMap, annotate);
|
2022-03-23 22:08:39 +01:00
|
|
|
ruleset = NULL;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_NETPROP_NONDEFRULE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
/*
|
|
|
|
|
* Differs from "TAPERRULE" in that it specifies a non-default
|
|
|
|
|
* rule to use for the entire net.
|
|
|
|
|
*/
|
|
|
|
|
he = HashLookOnly(&LefNonDefaultRules, token);
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
ruleset = (LefRules *)HashGetValue(he);
|
|
|
|
|
else
|
|
|
|
|
LefError(DEF_ERROR, "Unknown nondefault rule \"%s\"\n", token);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_NETPROP_PROPERTY:
|
|
|
|
|
/* Ignore except to absorb the next two tokens. */
|
|
|
|
|
token = LefNextToken(f, TRUE); /* Drop through */
|
|
|
|
|
|
|
|
|
|
case DEF_NETPROP_SOURCE:
|
|
|
|
|
case DEF_NETPROP_USE:
|
|
|
|
|
case DEF_NETPROP_SHIELDNET:
|
|
|
|
|
case DEF_NETPROP_SUBNET:
|
|
|
|
|
case DEF_NETPROP_XTALK:
|
|
|
|
|
case DEF_NETPROP_FREQUENCY:
|
|
|
|
|
case DEF_NETPROP_ORIGINAL:
|
|
|
|
|
case DEF_NETPROP_PATTERN:
|
|
|
|
|
case DEF_NETPROP_ESTCAP:
|
|
|
|
|
case DEF_NETPROP_WEIGHT:
|
|
|
|
|
/* Ignore except to absorb the next token. */
|
|
|
|
|
token = LefNextToken(f, TRUE); /* Drop through */
|
|
|
|
|
|
|
|
|
|
case DEF_NETPROP_FIXEDBUMP:
|
|
|
|
|
/* Ignore this keyword */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_NETPROP_VPIN:
|
|
|
|
|
/* VPIN is an in-line pin not in the PINS section */
|
|
|
|
|
/* Need to handle this! */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-05 20:29:54 +01:00
|
|
|
if (dolabels) freeMagic(netname);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_NET_END:
|
|
|
|
|
if (!LefParseEndStatement(f, sname))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Net END statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == DEF_NET_END) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processed == total)
|
|
|
|
|
TxPrintf(" Processed %d%s nets total.\n", processed,
|
|
|
|
|
(special) ? " special" : "");
|
|
|
|
|
else
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_WARNING, "Number of nets read (%d) does not match "
|
2017-04-25 14:41:48 +02:00
|
|
|
"the number declared (%d).\n", processed, total);
|
|
|
|
|
|
|
|
|
|
freeMagic((char *)defLayerMap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefReadUseLocation --
|
|
|
|
|
*
|
|
|
|
|
* Read location and orientation of a cell use
|
|
|
|
|
* Syntax: ( X Y ) O
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* 0 on success, -1 on failure
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Transform is placed in the location pointed to by "tptr".
|
|
|
|
|
* This routine can be used for placement of geometry (as
|
|
|
|
|
* opposed to cell uses) by setting "use" to NULL, and using
|
|
|
|
|
* the resulting transform to modify the geometry.
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
enum def_orient {DEF_NORTH, DEF_SOUTH, DEF_EAST, DEF_WEST,
|
|
|
|
|
DEF_FLIPPED_NORTH, DEF_FLIPPED_SOUTH, DEF_FLIPPED_EAST,
|
|
|
|
|
DEF_FLIPPED_WEST};
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-04 09:50:40 +01:00
|
|
|
DefReadLocation(
|
|
|
|
|
CellUse *use,
|
|
|
|
|
FILE *f,
|
|
|
|
|
float oscale,
|
|
|
|
|
Transform *tptr,
|
|
|
|
|
bool noplace)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-10-12 15:01:10 +02:00
|
|
|
const Rect *r;
|
|
|
|
|
Rect tr, rect;
|
2017-04-25 14:41:48 +02:00
|
|
|
int keyword;
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
float x, y;
|
|
|
|
|
Transform t2;
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const orientations[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"N", "S", "E", "W", "FN", "FS", "FE", "FW"
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-29 15:51:48 +02:00
|
|
|
if (noplace)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-07-29 15:51:48 +02:00
|
|
|
LefError(DEF_WARNING, "Unplaced component \"%s\" will be put at origin.\n",
|
|
|
|
|
use->cu_id);
|
|
|
|
|
x = 0;
|
|
|
|
|
y = 0;
|
|
|
|
|
keyword = DEF_NORTH;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (*token != '(') goto parse_error;
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%f", &x) != 1) goto parse_error;
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%f", &y) != 1) goto parse_error;
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (*token != ')') goto parse_error;
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, orientations);
|
2019-07-29 15:51:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Unknown macro orientation \"%s\".\n", token);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The standard transformations are all defined to rotate */
|
|
|
|
|
/* or flip about the origin. However, DEF defines them */
|
|
|
|
|
/* around the center such that the lower left corner is */
|
|
|
|
|
/* unchanged after the transformation. Case conditions */
|
|
|
|
|
/* restore the lower-left corner position. */
|
|
|
|
|
|
|
|
|
|
if (use)
|
2019-03-23 00:58:47 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
r = &use->cu_def->cd_bbox;
|
2019-03-23 00:58:47 +01:00
|
|
|
|
|
|
|
|
/* Abstract views with fixed bounding boxes use the FIXED_BBOX property */
|
|
|
|
|
|
|
|
|
|
if (use->cu_def->cd_flags & CDFIXEDBBOX)
|
|
|
|
|
{
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
PropertyRecord *proprec;
|
2019-03-23 00:58:47 +01:00
|
|
|
bool found;
|
|
|
|
|
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
proprec = DBPropGet(use->cu_def, "FIXED_BBOX", &found);
|
2019-03-23 00:58:47 +01:00
|
|
|
if (found)
|
|
|
|
|
{
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
|
|
|
|
|
(proprec->prop_len == 4))
|
|
|
|
|
{
|
|
|
|
|
rect.r_xbot = proprec->prop_value.prop_integer[0];
|
|
|
|
|
rect.r_ybot = proprec->prop_value.prop_integer[1];
|
|
|
|
|
rect.r_xtop = proprec->prop_value.prop_integer[2];
|
|
|
|
|
rect.r_ytop = proprec->prop_value.prop_integer[3];
|
2019-03-23 00:58:47 +01:00
|
|
|
r = ▭
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
}
|
2019-03-23 00:58:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
r = &GeoNullRect;
|
|
|
|
|
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_NORTH:
|
|
|
|
|
*tptr = GeoIdentityTransform;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_SOUTH:
|
|
|
|
|
*tptr = Geo180Transform;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_EAST:
|
|
|
|
|
*tptr = Geo90Transform;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_WEST:
|
|
|
|
|
*tptr = Geo270Transform;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_FLIPPED_NORTH:
|
|
|
|
|
*tptr = GeoSidewaysTransform;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_FLIPPED_SOUTH:
|
|
|
|
|
*tptr = GeoUpsideDownTransform;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_FLIPPED_EAST:
|
|
|
|
|
*tptr = GeoRef135Transform;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_FLIPPED_WEST:
|
|
|
|
|
*tptr = GeoRef45Transform;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
GeoTransRect(tptr, r, &tr);
|
|
|
|
|
GeoTranslateTrans(tptr, -tr.r_xbot, -tr.r_ybot, &t2);
|
|
|
|
|
GeoTranslateTrans(&t2, (int)roundf(x / oscale), (int)roundf(y / oscale), tptr);
|
|
|
|
|
if (use)
|
|
|
|
|
DBSetTrans(use, tptr);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
parse_error:
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Cannot parse location: must be ( X Y ) orient\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefReadPins --
|
|
|
|
|
*
|
|
|
|
|
* Read a PINS section from a DEF file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Generates paint and labels in the layout.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum def_pins_keys {DEF_PINS_START = 0, DEF_PINS_END};
|
|
|
|
|
enum def_pins_prop_keys {
|
|
|
|
|
DEF_PINS_PROP_NET = 0, DEF_PINS_PROP_DIR,
|
|
|
|
|
DEF_PINS_PROP_LAYER, DEF_PINS_PROP_USE,
|
2019-07-03 19:58:13 +02:00
|
|
|
DEF_PINS_PROP_PLACED, DEF_PINS_PROP_FIXED,
|
2025-08-04 22:13:36 +02:00
|
|
|
DEF_PINS_PROP_COVER, DEF_PINS_PROP_PORT,
|
|
|
|
|
DEF_PINS_PROP_SPECIAL};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
DefReadPins(
|
|
|
|
|
FILE *f,
|
|
|
|
|
CellDef *rootDef,
|
|
|
|
|
const char *sname,
|
|
|
|
|
float oscale,
|
|
|
|
|
int total,
|
|
|
|
|
bool annotate)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
char pinname[LEF_LINE_MAX];
|
2025-01-04 10:03:33 +01:00
|
|
|
int keyword, subkey, flags;
|
2017-04-25 14:41:48 +02:00
|
|
|
int processed = 0;
|
|
|
|
|
int pinDir = PORT_CLASS_DEFAULT;
|
2020-12-07 16:39:39 +01:00
|
|
|
int pinUse = PORT_USE_DEFAULT;
|
2017-08-02 04:14:42 +02:00
|
|
|
int pinNum = 0;
|
2022-04-01 02:02:12 +02:00
|
|
|
int width, height, rot, size;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType curlayer = -1;
|
2021-09-15 20:45:14 +02:00
|
|
|
LinkedRect *rectList = NULL, *newRect;
|
2017-04-25 14:41:48 +02:00
|
|
|
Rect *currect, topRect;
|
|
|
|
|
Transform t;
|
|
|
|
|
bool pending = FALSE;
|
2019-07-03 19:58:13 +02:00
|
|
|
bool hasports = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"-",
|
|
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_property_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"NET",
|
|
|
|
|
"DIRECTION",
|
|
|
|
|
"LAYER",
|
|
|
|
|
"USE",
|
|
|
|
|
"PLACED",
|
|
|
|
|
"FIXED",
|
2025-08-04 22:13:36 +02:00
|
|
|
"COVER",
|
2019-07-03 19:58:13 +02:00
|
|
|
"PORT",
|
|
|
|
|
"SPECIAL",
|
2017-04-25 14:41:48 +02:00
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_classes[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"DEFAULT",
|
|
|
|
|
"INPUT",
|
|
|
|
|
"OUTPUT TRISTATE",
|
|
|
|
|
"OUTPUT",
|
|
|
|
|
"INOUT",
|
|
|
|
|
"FEEDTHRU",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const pin_uses[] = {
|
2020-12-07 16:39:39 +01:00
|
|
|
"DEFAULT",
|
|
|
|
|
"SIGNAL",
|
|
|
|
|
"POWER",
|
|
|
|
|
"GROUND",
|
|
|
|
|
"CLOCK",
|
|
|
|
|
"RESET",
|
|
|
|
|
"ANALOG",
|
|
|
|
|
"SCAN",
|
|
|
|
|
"TIEOFF",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-18 14:52:14 +01:00
|
|
|
static const int lef_class_to_bitmask[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
PORT_CLASS_DEFAULT,
|
|
|
|
|
PORT_CLASS_INPUT,
|
|
|
|
|
PORT_CLASS_TRISTATE,
|
|
|
|
|
PORT_CLASS_OUTPUT,
|
|
|
|
|
PORT_CLASS_BIDIRECTIONAL,
|
|
|
|
|
PORT_CLASS_FEEDTHROUGH
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-18 14:52:14 +01:00
|
|
|
static const int lef_use_to_bitmask[] = {
|
2020-12-07 16:39:39 +01:00
|
|
|
PORT_USE_DEFAULT,
|
|
|
|
|
PORT_USE_SIGNAL,
|
|
|
|
|
PORT_USE_POWER,
|
|
|
|
|
PORT_USE_GROUND,
|
|
|
|
|
PORT_USE_CLOCK,
|
|
|
|
|
PORT_USE_RESET,
|
|
|
|
|
PORT_USE_ANALOG,
|
|
|
|
|
PORT_USE_SCAN,
|
|
|
|
|
PORT_USE_TIEOFF
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
flags = 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, pin_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in PINS "
|
2017-04-25 14:41:48 +02:00
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_PINS_START: /* "-" keyword */
|
2019-07-03 19:58:13 +02:00
|
|
|
hasports = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
// Flag an error if a pin was waiting on a layer
|
|
|
|
|
// specification that was never given.
|
|
|
|
|
|
|
|
|
|
if (pending)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Pin specified without layer, was not placed.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update the record of the number of pins */
|
|
|
|
|
/* processed and spit out a message for every 5% done. */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEstimate(processed++, total, "pins");
|
|
|
|
|
|
|
|
|
|
/* Get pin name */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%2047s", pinname) != 1)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Bad pin statement: Need pin name\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
pending = FALSE;
|
2020-08-08 20:02:29 +02:00
|
|
|
curlayer = -1;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Now do a search through the line for "+" entries */
|
|
|
|
|
/* And process each. */
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (*token == ';') break;
|
|
|
|
|
if (*token != '+') continue;
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, pin_property_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subkey < 0)
|
|
|
|
|
{
|
2019-07-03 19:58:13 +02:00
|
|
|
LefError(DEF_INFO, "Unknown pin property \"%s\" in "
|
2017-04-25 14:41:48 +02:00
|
|
|
"PINS definition; ignoring.\n", token);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (subkey)
|
|
|
|
|
{
|
2019-07-03 19:58:13 +02:00
|
|
|
case DEF_PINS_PROP_SPECIAL:
|
|
|
|
|
/* Ignore this */
|
|
|
|
|
break;
|
|
|
|
|
case DEF_PINS_PROP_PORT:
|
|
|
|
|
/* Ignore this, except that each port adds to */
|
|
|
|
|
/* the count of total pins processed. Note */
|
|
|
|
|
/* that since "processed" is incremented before */
|
|
|
|
|
/* the first PORT is seen, then "processed" */
|
|
|
|
|
/* should not be incremented until the 2nd PORT */
|
|
|
|
|
if (hasports) processed++;
|
|
|
|
|
hasports = TRUE;
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEF_PINS_PROP_USE:
|
2020-12-07 16:39:39 +01:00
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, pin_uses);
|
2020-12-07 16:39:39 +01:00
|
|
|
if (subkey < 0)
|
|
|
|
|
LefError(DEF_ERROR, "Unknown pin use \"%s\"\n", token);
|
|
|
|
|
else
|
|
|
|
|
pinUse = lef_use_to_bitmask[subkey];
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEF_PINS_PROP_NET:
|
|
|
|
|
/* Get the net name, but ignore it */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_PINS_PROP_DIR:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, pin_classes);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subkey < 0)
|
2020-12-07 16:39:39 +01:00
|
|
|
LefError(DEF_ERROR, "Unknown pin class \"%s\"\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
pinDir = lef_class_to_bitmask[subkey];
|
|
|
|
|
break;
|
|
|
|
|
case DEF_PINS_PROP_LAYER:
|
|
|
|
|
curlayer = LefReadLayer(f, FALSE);
|
|
|
|
|
currect = LefReadRect(f, curlayer, oscale);
|
2021-09-15 20:45:14 +02:00
|
|
|
|
|
|
|
|
newRect = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
|
|
|
|
|
newRect->r_type = curlayer;
|
|
|
|
|
newRect->r_r = *currect;
|
|
|
|
|
newRect->r_next = rectList;
|
|
|
|
|
rectList = newRect;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (pending)
|
|
|
|
|
{
|
2019-02-14 18:19:50 +01:00
|
|
|
/* If layer was unknown, set to space and force */
|
|
|
|
|
/* non-sticky. */
|
2019-03-23 00:58:47 +01:00
|
|
|
flags = PORT_DIR_MASK;
|
2019-02-14 18:19:50 +01:00
|
|
|
if (curlayer < 0)
|
|
|
|
|
curlayer = TT_SPACE;
|
|
|
|
|
else
|
|
|
|
|
flags |= LABEL_STICKY;
|
|
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2021-09-15 20:45:14 +02:00
|
|
|
while (rectList != NULL)
|
|
|
|
|
{
|
|
|
|
|
GeoTransRect(&t, &rectList->r_r, &topRect);
|
|
|
|
|
DBPaint(rootDef, &topRect, rectList->r_type);
|
2022-04-01 02:02:12 +02:00
|
|
|
// DBPutLabel(rootDef, &topRect, -1, pinname,
|
|
|
|
|
// rectList->r_type,
|
|
|
|
|
// pinDir | pinUse | flags, pinNum);
|
|
|
|
|
width = (topRect.r_xtop - topRect.r_xbot);
|
|
|
|
|
height = (topRect.r_ytop - topRect.r_ybot);
|
|
|
|
|
rot = 0;
|
|
|
|
|
if (height > (width * 2))
|
|
|
|
|
{
|
|
|
|
|
int temp = height;
|
|
|
|
|
height = width;
|
|
|
|
|
width = temp;
|
|
|
|
|
rot = 90;
|
|
|
|
|
}
|
|
|
|
|
size = DRCGetDefaultLayerWidth(rectList->r_type);
|
|
|
|
|
while ((size << 1) < height) size <<= 1;
|
|
|
|
|
size <<= 3; /* Fonts are in 8x units */
|
|
|
|
|
DBPutFontLabel(rootDef, &topRect,
|
|
|
|
|
0, size, rot, &GeoOrigin,
|
|
|
|
|
GEO_CENTER, pinname,
|
|
|
|
|
rectList->r_type,
|
|
|
|
|
pinDir | pinUse | flags, pinNum);
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, rectList);
|
2021-09-15 20:45:14 +02:00
|
|
|
rectList = rectList->r_next;
|
|
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
pending = FALSE;
|
2017-08-02 04:14:42 +02:00
|
|
|
pinNum++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DEF_PINS_PROP_FIXED:
|
2025-08-04 22:13:36 +02:00
|
|
|
case DEF_PINS_PROP_COVER:
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEF_PINS_PROP_PLACED:
|
2019-07-29 15:51:48 +02:00
|
|
|
DefReadLocation(NULL, f, oscale, &t, FALSE);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (curlayer == -1)
|
|
|
|
|
pending = TRUE;
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-02-14 18:19:50 +01:00
|
|
|
/* If layer was unknown, set to space and force */
|
|
|
|
|
/* non-sticky. */
|
2019-03-23 00:58:47 +01:00
|
|
|
flags = PORT_DIR_MASK;
|
2019-02-14 18:19:50 +01:00
|
|
|
if (curlayer < 0)
|
|
|
|
|
curlayer = TT_SPACE;
|
|
|
|
|
else
|
|
|
|
|
flags |= LABEL_STICKY;
|
|
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2021-09-15 20:45:14 +02:00
|
|
|
while (rectList != NULL)
|
|
|
|
|
{
|
|
|
|
|
GeoTransRect(&t, &rectList->r_r, &topRect);
|
|
|
|
|
DBPaint(rootDef, &topRect, rectList->r_type);
|
2022-04-01 02:02:12 +02:00
|
|
|
// DBPutLabel(rootDef, &topRect, -1, pinname,
|
|
|
|
|
// rectList->r_type,
|
|
|
|
|
// pinDir | pinUse | flags, pinNum);
|
|
|
|
|
width = (topRect.r_xtop - topRect.r_xbot);
|
|
|
|
|
height = (topRect.r_ytop - topRect.r_ybot);
|
|
|
|
|
rot = 0;
|
|
|
|
|
if (height > (width * 2))
|
|
|
|
|
{
|
|
|
|
|
int temp = height;
|
|
|
|
|
height = width;
|
|
|
|
|
width = temp;
|
|
|
|
|
rot = 90;
|
|
|
|
|
}
|
|
|
|
|
size = DRCGetDefaultLayerWidth(rectList->r_type);
|
|
|
|
|
while ((size << 1) < height) size <<= 1;
|
|
|
|
|
size <<= 3; /* Fonts are in 8x units */
|
2022-09-14 19:02:55 +02:00
|
|
|
|
|
|
|
|
/* If DEF file is being imported to annotate a
|
|
|
|
|
* layout, then remove any existing label in
|
|
|
|
|
* the layout that matches the PIN record.
|
|
|
|
|
*/
|
|
|
|
|
if (annotate)
|
|
|
|
|
DBEraseLabelsByContent(rootDef, &topRect,
|
|
|
|
|
-1, pinname);
|
|
|
|
|
|
2022-04-01 02:02:12 +02:00
|
|
|
DBPutFontLabel(rootDef, &topRect,
|
|
|
|
|
0, size, rot, &GeoOrigin,
|
|
|
|
|
GEO_CENTER, pinname,
|
|
|
|
|
rectList->r_type,
|
|
|
|
|
pinDir | pinUse | flags, pinNum);
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, rectList);
|
2021-09-15 20:45:14 +02:00
|
|
|
rectList = rectList->r_next;
|
|
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2021-09-15 20:45:14 +02:00
|
|
|
pending = FALSE;
|
2017-08-02 04:14:42 +02:00
|
|
|
pinNum++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_PINS_END:
|
|
|
|
|
if (!LefParseEndStatement(f, sname))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Pins END statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == DEF_PINS_END) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processed == total)
|
|
|
|
|
TxPrintf(" Processed %d pins total.\n", processed);
|
|
|
|
|
else
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_WARNING, "Number of pins read (%d) does not match "
|
2017-04-25 14:41:48 +02:00
|
|
|
"the number declared (%d).\n", processed, total);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2022-03-24 22:58:05 +01:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefReadBlockages --
|
|
|
|
|
*
|
|
|
|
|
* Read a BLOCKAGES section from a DEF file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Generates layout
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum def_block_keys {DEF_BLOCK_START = 0, DEF_BLOCK_END};
|
|
|
|
|
enum def_block_prop_keys {
|
|
|
|
|
DEF_BLOCK_PROP_RECT = 0, DEF_BLOCK_PROP_LAYER};
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
DefReadBlockages(
|
|
|
|
|
FILE *f,
|
|
|
|
|
CellDef *rootDef,
|
|
|
|
|
const char *sname,
|
|
|
|
|
float oscale,
|
|
|
|
|
int total)
|
2022-03-24 22:58:05 +01:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2025-01-04 10:03:33 +01:00
|
|
|
int keyword, subkey;
|
2022-03-24 22:58:05 +01:00
|
|
|
int processed = 0;
|
|
|
|
|
TileType curlayer;
|
|
|
|
|
Rect *currect;
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const block_keys[] = {
|
2022-03-24 22:58:05 +01:00
|
|
|
"-",
|
|
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const block_property_keys[] = {
|
2022-03-24 22:58:05 +01:00
|
|
|
"RECT",
|
|
|
|
|
"LAYER",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, block_keys);
|
2022-03-24 22:58:05 +01:00
|
|
|
|
|
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in BLOCKAGES "
|
|
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_BLOCK_START: /* "-" keyword */
|
|
|
|
|
|
|
|
|
|
/* Update the record of the number of blockages */
|
|
|
|
|
/* processed and spit out a message for every 5% done. */
|
|
|
|
|
|
|
|
|
|
LefEstimate(processed++, total, "blockages");
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (*token == ';')
|
|
|
|
|
break;
|
|
|
|
|
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, block_property_keys);
|
2022-03-24 22:58:05 +01:00
|
|
|
if (subkey < 0)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_INFO, "Unknown blockage property \"%s\" in "
|
|
|
|
|
"BLOCKAGES definition; ignoring.\n", token);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (subkey)
|
|
|
|
|
{
|
|
|
|
|
case DEF_BLOCK_PROP_LAYER:
|
|
|
|
|
curlayer = LefReadLayer(f, TRUE);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_BLOCK_PROP_RECT:
|
|
|
|
|
currect = LefReadRect(f, curlayer, oscale);
|
|
|
|
|
DBPaint(rootDef, currect, curlayer);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_BLOCK_END:
|
|
|
|
|
if (!LefParseEndStatement(f, sname))
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Blockage END statement missing.\n");
|
|
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == DEF_BLOCK_END) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processed == total)
|
|
|
|
|
TxPrintf(" Processed %d blockage%s.\n", processed,
|
|
|
|
|
((processed > 1) ? "s" : ""));
|
|
|
|
|
else
|
|
|
|
|
LefError(DEF_WARNING, "Number of blockages read (%d) does not match "
|
|
|
|
|
"the number declared (%d).\n", processed, total);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefReadVias --
|
|
|
|
|
*
|
|
|
|
|
* Read a VIAS section from a DEF file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Technically, this routine should be creating a cell for
|
|
|
|
|
* each defined via. For now, it just computes the bounding
|
|
|
|
|
* rectangle and layer.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum def_vias_keys {DEF_VIAS_START = 0, DEF_VIAS_END};
|
|
|
|
|
enum def_vias_prop_keys {
|
2019-07-03 19:58:13 +02:00
|
|
|
DEF_VIAS_PROP_RECT = 0, DEF_VIAS_PROP_VIARULE,
|
|
|
|
|
DEF_VIAS_PROP_CUTSIZE, DEF_VIAS_PROP_LAYERS,
|
|
|
|
|
DEF_VIAS_PROP_CUTSPACING, DEF_VIAS_PROP_ENCLOSURE,
|
|
|
|
|
DEF_VIAS_PROP_ROWCOL};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
DefReadVias(
|
|
|
|
|
FILE *f,
|
|
|
|
|
const char *sname,
|
|
|
|
|
float oscale,
|
|
|
|
|
int total)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
2017-04-25 14:41:48 +02:00
|
|
|
char vianame[LEF_LINE_MAX];
|
2025-01-04 10:03:33 +01:00
|
|
|
int keyword, subkey;
|
2017-04-25 14:41:48 +02:00
|
|
|
int processed = 0;
|
|
|
|
|
TileType curlayer;
|
|
|
|
|
lefLayer *lefl;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
/* For generated vias */
|
|
|
|
|
bool generated = FALSE;
|
|
|
|
|
int sizex, sizey, spacex, spacey;
|
|
|
|
|
int encbx, encby, enctx, encty;
|
|
|
|
|
int rows = 1, cols = 1;
|
|
|
|
|
TileType tlayer, clayer, blayer;
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const via_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"-",
|
|
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const via_property_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"RECT",
|
2019-07-03 19:58:13 +02:00
|
|
|
"VIARULE",
|
|
|
|
|
"CUTSIZE",
|
|
|
|
|
"LAYERS",
|
|
|
|
|
"CUTSPACING",
|
|
|
|
|
"ENCLOSURE",
|
|
|
|
|
"ROWCOL",
|
2017-04-25 14:41:48 +02:00
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, via_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in VIAS "
|
2017-04-25 14:41:48 +02:00
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_VIAS_START: /* "-" keyword */
|
|
|
|
|
|
|
|
|
|
/* Update the record of the number of vias */
|
|
|
|
|
/* processed and spit out a message for every 5% done. */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEstimate(processed++, total, "vias");
|
|
|
|
|
|
2020-03-21 15:16:33 +01:00
|
|
|
/* If not otherwise specified, rows and columns default to 1 */
|
|
|
|
|
rows = cols = 1;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Get via name */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%2047s", vianame) != 1)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Bad via statement: Need via name\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
he = HashFind(&LefInfo, vianame);
|
|
|
|
|
lefl = (lefLayer *)HashGetValue(he);
|
|
|
|
|
if (lefl == NULL)
|
|
|
|
|
{
|
|
|
|
|
lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
|
|
|
|
|
lefl->type = -1;
|
|
|
|
|
lefl->obsType = -1;
|
|
|
|
|
lefl->lefClass = CLASS_VIA;
|
|
|
|
|
lefl->info.via.area = GeoNullRect;
|
|
|
|
|
lefl->info.via.cell = (CellDef *)NULL;
|
|
|
|
|
lefl->info.via.lr = (LinkedRect *)NULL;
|
|
|
|
|
HashSetValue(he, lefl);
|
2025-01-04 10:05:56 +01:00
|
|
|
lefl->canonName = (const char *)he->h_key.h_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_WARNING, "Composite via \"%s\" redefined.\n", vianame);
|
2017-04-25 14:41:48 +02:00
|
|
|
lefl = LefRedefined(lefl, vianame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now do a search through the line for "+" entries */
|
|
|
|
|
/* And process each. */
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2019-07-03 19:58:13 +02:00
|
|
|
if (*token == ';') {
|
|
|
|
|
if (generated == TRUE) {
|
|
|
|
|
/* Complete the generated via */
|
|
|
|
|
LefGenViaGeometry(f, lefl,
|
|
|
|
|
sizex, sizey, spacex, spacey,
|
|
|
|
|
encbx, encby, enctx, encty,
|
|
|
|
|
rows, cols, tlayer, clayer, blayer,
|
|
|
|
|
oscale);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (*token != '+') continue;
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, via_property_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subkey < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown via property \"%s\" in "
|
2017-04-25 14:41:48 +02:00
|
|
|
"VIAS definition; ignoring.\n", token);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (subkey)
|
|
|
|
|
{
|
|
|
|
|
case DEF_VIAS_PROP_RECT:
|
|
|
|
|
curlayer = LefReadLayer(f, FALSE);
|
|
|
|
|
LefAddViaGeometry(f, lefl, curlayer, oscale);
|
|
|
|
|
break;
|
2019-07-03 19:58:13 +02:00
|
|
|
|
|
|
|
|
case DEF_VIAS_PROP_VIARULE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
/* Ignore this. To do: Parse VIARULE statements */
|
|
|
|
|
/* and use the rule to fill any missing values. */
|
|
|
|
|
break;
|
|
|
|
|
case DEF_VIAS_PROP_CUTSIZE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &sizex) != 1)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for CUTSIZE.\n");
|
|
|
|
|
/* To do: Get cut size from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &sizey) != 1)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for CUTSIZE.\n");
|
|
|
|
|
/* To do: Get cut size from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
generated = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_VIAS_PROP_LAYERS:
|
|
|
|
|
blayer = LefReadLayer(f, FALSE);
|
|
|
|
|
clayer = LefReadLayer(f, FALSE);
|
|
|
|
|
tlayer = LefReadLayer(f, FALSE);
|
2020-03-03 23:13:37 +01:00
|
|
|
|
|
|
|
|
/* Provisional behavior: A known tool generating */
|
|
|
|
|
/* DEF uses the order (bottom, top, cut). This may */
|
|
|
|
|
/* be a bug in the tool and an issue is being */
|
|
|
|
|
/* raised. However, there is no harm in detecting */
|
|
|
|
|
/* which layer is the cut and swapping as needed. */
|
|
|
|
|
|
|
|
|
|
if (!DBIsContact(clayer))
|
|
|
|
|
{
|
|
|
|
|
TileType swaplayer;
|
|
|
|
|
LefError(DEF_WARNING, "Improper layer order for"
|
|
|
|
|
" VIARULE.\n");
|
|
|
|
|
if (DBIsContact(tlayer))
|
|
|
|
|
{
|
|
|
|
|
swaplayer = clayer;
|
|
|
|
|
clayer = tlayer;
|
|
|
|
|
tlayer = swaplayer;
|
|
|
|
|
}
|
|
|
|
|
else if (DBIsContact(blayer))
|
|
|
|
|
{
|
|
|
|
|
swaplayer = clayer;
|
|
|
|
|
clayer = blayer;
|
|
|
|
|
blayer = swaplayer;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
LefError(DEF_ERROR, "No cut layer specified in"
|
|
|
|
|
" VIARULE.\n");
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 19:58:13 +02:00
|
|
|
generated = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_VIAS_PROP_CUTSPACING:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &spacex) != 1)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for CUTSPACING.\n");
|
|
|
|
|
/* To do: Get cut spacing from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &spacey) != 1)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for CUTSPACING.\n");
|
|
|
|
|
/* To do: Get cut spacing from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
generated = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_VIAS_PROP_ENCLOSURE:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2020-05-28 00:02:36 +02:00
|
|
|
if (sscanf(token, "%d", &encbx) != 1)
|
2019-07-03 19:58:13 +02:00
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for ENCLOSURE.\n");
|
|
|
|
|
/* To do: Get cut enclosures from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2020-05-28 00:02:36 +02:00
|
|
|
if (sscanf(token, "%d", &encby) != 1)
|
2019-07-03 19:58:13 +02:00
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for ENCLOSURE.\n");
|
|
|
|
|
/* To do: Get cut enclosures from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2020-05-28 00:02:36 +02:00
|
|
|
if (sscanf(token, "%d", &enctx) != 1)
|
2019-07-03 19:58:13 +02:00
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for ENCLOSURE.\n");
|
|
|
|
|
/* To do: Get cut enclosures from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2020-05-28 00:02:36 +02:00
|
|
|
if (sscanf(token, "%d", &encty) != 1)
|
2019-07-03 19:58:13 +02:00
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for ENCLOSURE.\n");
|
|
|
|
|
/* To do: Get cut enclosures from DRC ruleset */
|
|
|
|
|
}
|
|
|
|
|
generated = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case DEF_VIAS_PROP_ROWCOL:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &rows) != 1)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for ROWCOL.\n");
|
|
|
|
|
rows = 1;
|
|
|
|
|
}
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &cols) != 1)
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_ERROR, "Invalid syntax for ROWCOL.\n");
|
|
|
|
|
cols = 1;
|
|
|
|
|
}
|
|
|
|
|
generated = TRUE;
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_VIAS_END:
|
|
|
|
|
if (!LefParseEndStatement(f, sname))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Vias END statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == DEF_VIAS_END) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processed == total)
|
|
|
|
|
TxPrintf(" Processed %d vias total.\n", processed);
|
|
|
|
|
else
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_WARNING, "Number of vias read (%d) does not match "
|
2017-04-25 14:41:48 +02:00
|
|
|
"the number declared (%d).\n", processed, total);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefReadComponents --
|
|
|
|
|
*
|
|
|
|
|
* Read a COMPONENTS section from a DEF file.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Many. Cell definitions and uses are created and added to
|
|
|
|
|
* the database.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
enum def_comp_keys {DEF_COMP_START = 0, DEF_COMP_END};
|
|
|
|
|
enum def_prop_keys {
|
|
|
|
|
DEF_PROP_FIXED = 0, DEF_PROP_COVER,
|
|
|
|
|
DEF_PROP_PLACED, DEF_PROP_UNPLACED,
|
|
|
|
|
DEF_PROP_SOURCE, DEF_PROP_WEIGHT, DEF_PROP_FOREIGN,
|
|
|
|
|
DEF_PROP_REGION, DEF_PROP_GENERATE, DEF_PROP_PROPERTY,
|
|
|
|
|
DEF_PROP_EEQMASTER};
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-04 09:50:40 +01:00
|
|
|
DefReadComponents(
|
|
|
|
|
FILE *f,
|
|
|
|
|
CellDef *rootDef,
|
|
|
|
|
const char *sname,
|
|
|
|
|
float oscale,
|
|
|
|
|
int total)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
CellDef *defMacro;
|
|
|
|
|
CellUse *defUse;
|
|
|
|
|
Transform t;
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
|
|
|
|
char *dptr;
|
2025-11-23 20:14:21 +01:00
|
|
|
char *usename, usename_buf[512];
|
|
|
|
|
int usename_len;
|
2025-01-04 10:03:33 +01:00
|
|
|
int keyword, subkey;
|
2017-04-25 14:41:48 +02:00
|
|
|
int processed = 0;
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const component_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"-",
|
|
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const property_keys[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"FIXED",
|
|
|
|
|
"COVER",
|
|
|
|
|
"PLACED",
|
|
|
|
|
"UNPLACED",
|
|
|
|
|
"SOURCE",
|
|
|
|
|
"WEIGHT",
|
|
|
|
|
"FOREIGN",
|
|
|
|
|
"REGION",
|
|
|
|
|
"GENERATE",
|
|
|
|
|
"PROPERTY",
|
|
|
|
|
"EEQMASTER",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, component_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in COMPONENT "
|
2017-04-25 14:41:48 +02:00
|
|
|
"definition; ignoring.\n", token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_COMP_START: /* "-" keyword */
|
|
|
|
|
|
|
|
|
|
/* Update the record of the number of components */
|
|
|
|
|
/* processed and spit out a message for every 5% done. */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEstimate(processed++, total, "subcell instances");
|
|
|
|
|
|
|
|
|
|
/* Get use and macro names */
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2025-11-23 20:14:21 +01:00
|
|
|
|
|
|
|
|
usename_len = strlen(token);
|
|
|
|
|
if (!usename_len)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Bad component statement: Need "
|
|
|
|
|
"use and macro names\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-07-20 04:58:45 +02:00
|
|
|
|
2025-11-23 20:14:21 +01:00
|
|
|
/* Use stack buffer if we can , else go for the heap */
|
|
|
|
|
if (strlen(token) < sizeof(usename_buf))
|
|
|
|
|
{
|
|
|
|
|
strcpy(usename_buf, token);
|
|
|
|
|
usename = usename_buf;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
usename = StrDup(NULL, token);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 20:51:46 +01:00
|
|
|
/* Magic prohibits slashes and commas in use names */
|
|
|
|
|
/* when using the "identify" command. Removing these */
|
|
|
|
|
/* restrictions (at least the slash) is quite complex, */
|
|
|
|
|
/* but really should be taken care of, since no other */
|
|
|
|
|
/* tools consider this an illegal use, that I'm aware */
|
|
|
|
|
/* of. */
|
|
|
|
|
|
|
|
|
|
for (dptr = usename; *dptr; dptr++)
|
|
|
|
|
if ((*dptr == '/') || (*dptr == ','))
|
|
|
|
|
{
|
|
|
|
|
LefError(DEF_WARNING, "Character in instance name "
|
|
|
|
|
"converted to underscore.\n");
|
2019-07-25 16:20:24 +02:00
|
|
|
*dptr = '_';
|
|
|
|
|
}
|
2019-07-20 04:58:45 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
|
|
|
|
|
/* Find the corresponding macro definition */
|
|
|
|
|
defUse = NULL;
|
|
|
|
|
defMacro = DBCellLookDef(token);
|
|
|
|
|
|
|
|
|
|
if (defMacro == (CellDef *)NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Before giving up, assume that this cell has a */
|
|
|
|
|
/* magic .mag layout file. */
|
2020-03-21 17:40:35 +01:00
|
|
|
defMacro = DBCellNewDef(token);
|
2017-04-25 14:41:48 +02:00
|
|
|
defMacro->cd_flags &= ~CDNOTFOUND;
|
2022-01-22 19:02:47 +01:00
|
|
|
|
2023-04-18 17:01:58 +02:00
|
|
|
if (!DBCellRead(defMacro, TRUE, TRUE, NULL))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Cell %s is not defined. Maybe you "
|
|
|
|
|
"have not read the corresponding LEF file?\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
token);
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
DBCellDeleteDef(defMacro);
|
|
|
|
|
defMacro = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
DBReComputeBbox(defMacro);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a use for this celldef in the edit cell */
|
|
|
|
|
/* Don't process properties for cells we could not find */
|
2025-11-23 20:14:21 +01:00
|
|
|
if (defMacro == NULL)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
defUse = DBCellNewUse(defMacro, usename);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2025-11-23 20:14:21 +01:00
|
|
|
if (usename != usename_buf)
|
|
|
|
|
freeMagic(usename);
|
|
|
|
|
usename = NULL;
|
|
|
|
|
|
|
|
|
|
if (defUse == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-11-23 20:14:21 +01:00
|
|
|
LefEndStatement(f);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
DBLinkCell(defUse, rootDef);
|
|
|
|
|
|
|
|
|
|
/* Now do a search through the line for "+" entries */
|
|
|
|
|
/* And process each. */
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (*token == ';') break;
|
|
|
|
|
if (*token != '+') continue;
|
|
|
|
|
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2023-08-25 15:48:24 +02:00
|
|
|
subkey = LookupFull(token, property_keys);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subkey < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown component property \"%s\" in "
|
2017-04-25 14:41:48 +02:00
|
|
|
"COMPONENT definition; ignoring.\n", token);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (subkey)
|
|
|
|
|
{
|
|
|
|
|
case DEF_PROP_PLACED:
|
|
|
|
|
case DEF_PROP_FIXED:
|
|
|
|
|
case DEF_PROP_COVER:
|
2019-07-29 15:51:48 +02:00
|
|
|
DefReadLocation(defUse, f, oscale, &t, FALSE);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_PROP_UNPLACED:
|
|
|
|
|
DefReadLocation(defUse, f, oscale, &t, TRUE);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEF_PROP_SOURCE:
|
|
|
|
|
case DEF_PROP_WEIGHT:
|
|
|
|
|
case DEF_PROP_FOREIGN:
|
|
|
|
|
case DEF_PROP_REGION:
|
|
|
|
|
case DEF_PROP_GENERATE:
|
|
|
|
|
case DEF_PROP_PROPERTY:
|
|
|
|
|
case DEF_PROP_EEQMASTER:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Place the cell */
|
|
|
|
|
if (defUse != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBPlaceCell(defUse, rootDef);
|
|
|
|
|
defUse = NULL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEF_COMP_END:
|
|
|
|
|
if (!LefParseEndStatement(f, sname))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Component END statement missing.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finish final call by placing the cell use */
|
|
|
|
|
if ((total > 0) && (defUse != NULL))
|
|
|
|
|
{
|
|
|
|
|
DBPlaceCell(defUse, rootDef);
|
|
|
|
|
defUse = NULL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == DEF_COMP_END) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (processed == total)
|
|
|
|
|
TxPrintf(" Processed %d subcell instances total.\n", processed);
|
|
|
|
|
else
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_WARNING, "Number of subcells read (%d) does not match "
|
2017-04-25 14:41:48 +02:00
|
|
|
"the number declared (%d).\n", processed, total);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 20:48:27 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
* DefNewCell --
|
|
|
|
|
*
|
|
|
|
|
* Create a new CellDef for the DEF file contents.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* Pointer to new CellDef
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* If a cell of the given name did not exist, then
|
|
|
|
|
* a new CellDef is allocated.
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
CellDef *
|
|
|
|
|
DefNewCell(
|
|
|
|
|
const char *name)
|
|
|
|
|
{
|
|
|
|
|
CellDef *newDef;
|
|
|
|
|
|
|
|
|
|
newDef = DBCellLookDef(name);
|
|
|
|
|
if (newDef == NULL)
|
|
|
|
|
{
|
|
|
|
|
newDef = DBCellNewDef(name);
|
|
|
|
|
DBReComputeBbox(newDef);
|
|
|
|
|
DBCellClearDef(newDef);
|
|
|
|
|
DBCellSetAvail(newDef);
|
|
|
|
|
}
|
|
|
|
|
return newDef;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* DefRead --
|
|
|
|
|
*
|
|
|
|
|
* Read a .def file into a magic layout.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Many. Cell definitions and uses are created and added to
|
|
|
|
|
* the database.
|
|
|
|
|
*
|
|
|
|
|
*------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Enumeration of sections defined in DEF files */
|
|
|
|
|
|
|
|
|
|
enum def_sections {DEF_VERSION = 0, DEF_NAMESCASESENSITIVE,
|
|
|
|
|
DEF_UNITS, DEF_DESIGN, DEF_REGIONS, DEF_ROW, DEF_TRACKS,
|
|
|
|
|
DEF_GCELLGRID, DEF_DIVIDERCHAR, DEF_BUSBITCHARS,
|
|
|
|
|
DEF_PROPERTYDEFINITIONS, DEF_DEFAULTCAP, DEF_TECHNOLOGY,
|
|
|
|
|
DEF_HISTORY, DEF_DIEAREA, DEF_COMPONENTS, DEF_VIAS,
|
|
|
|
|
DEF_PINS, DEF_PINPROPERTIES, DEF_SPECIALNETS,
|
|
|
|
|
DEF_NETS, DEF_IOTIMINGS, DEF_SCANCHAINS,
|
2021-01-13 10:15:43 +01:00
|
|
|
DEF_CONSTRAINTS, DEF_GROUPS, DEF_EXTENSION, DEF_BLOCKAGES,
|
2022-03-23 15:49:02 +01:00
|
|
|
DEF_NONDEFAULTRULES, DEF_END};
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2025-08-26 20:48:27 +02:00
|
|
|
CellDef *
|
2025-01-04 09:50:40 +01:00
|
|
|
DefRead(
|
2025-01-04 10:00:08 +01:00
|
|
|
const char *inName,
|
2025-01-04 09:50:40 +01:00
|
|
|
bool dolabels,
|
|
|
|
|
bool annotate,
|
|
|
|
|
bool noblockage)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
CellDef *rootDef;
|
|
|
|
|
FILE *f;
|
|
|
|
|
char *filename;
|
2025-02-18 14:45:36 +01:00
|
|
|
const char *token;
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
PropertyRecord *proprec;
|
2017-04-25 14:41:48 +02:00
|
|
|
int keyword, dscale, total;
|
|
|
|
|
float oscale;
|
2022-10-15 17:33:59 +02:00
|
|
|
Rect *dierect;
|
2025-08-26 20:48:27 +02:00
|
|
|
bool design_is_root = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-10 21:16:01 +02:00
|
|
|
static const char * const sections[] = {
|
2017-04-25 14:41:48 +02:00
|
|
|
"VERSION",
|
|
|
|
|
"NAMESCASESENSITIVE",
|
|
|
|
|
"UNITS",
|
|
|
|
|
"DESIGN",
|
|
|
|
|
"REGIONS",
|
|
|
|
|
"ROW",
|
|
|
|
|
"TRACKS",
|
|
|
|
|
"GCELLGRID",
|
|
|
|
|
"DIVIDERCHAR",
|
|
|
|
|
"BUSBITCHARS",
|
|
|
|
|
"PROPERTYDEFINITIONS",
|
|
|
|
|
"DEFAULTCAP",
|
|
|
|
|
"TECHNOLOGY",
|
|
|
|
|
"HISTORY",
|
|
|
|
|
"DIEAREA",
|
|
|
|
|
"COMPONENTS",
|
|
|
|
|
"VIAS",
|
|
|
|
|
"PINS",
|
|
|
|
|
"PINPROPERTIES",
|
|
|
|
|
"SPECIALNETS",
|
|
|
|
|
"NETS",
|
|
|
|
|
"IOTIMINGS",
|
|
|
|
|
"SCANCHAINS",
|
|
|
|
|
"CONSTRAINTS",
|
|
|
|
|
"GROUPS",
|
|
|
|
|
"BEGINEXT",
|
2021-01-13 10:15:43 +01:00
|
|
|
"BLOCKAGES",
|
2022-03-23 15:49:02 +01:00
|
|
|
"NONDEFAULTRULES",
|
2017-04-25 14:41:48 +02:00
|
|
|
"END",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-30 19:02:12 +02:00
|
|
|
/* "annotate" implies "dolabels" whether set or not */
|
|
|
|
|
if (annotate) dolabels = TRUE;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Make sure we have a valid LefInfo hash table, even if it's empty */
|
|
|
|
|
if (LefInfo.ht_table == (HashEntry **) NULL)
|
|
|
|
|
LefTechInit();
|
|
|
|
|
|
|
|
|
|
f = lefFileOpen(NULL, inName, ".def", "r", &filename);
|
|
|
|
|
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
TxError("Cannot open input file %s (%s).\n", filename,
|
|
|
|
|
strerror(errno));
|
|
|
|
|
#else
|
|
|
|
|
TxError("Cannot open input file: ");
|
|
|
|
|
perror(filename);
|
|
|
|
|
#endif
|
2025-08-26 20:48:27 +02:00
|
|
|
return NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize */
|
|
|
|
|
|
|
|
|
|
TxPrintf("Reading DEF data from file %s.\n", filename);
|
|
|
|
|
TxPrintf("This action cannot be undone.\n");
|
|
|
|
|
UndoDisable();
|
|
|
|
|
|
2025-08-26 20:48:27 +02:00
|
|
|
rootDef = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
oscale = CIFGetOutputScale(1000);
|
|
|
|
|
lefCurrentLine = 0;
|
|
|
|
|
|
|
|
|
|
/* Read file contents */
|
|
|
|
|
|
|
|
|
|
while ((token = LefNextToken(f, TRUE)) != NULL)
|
|
|
|
|
{
|
2023-08-25 15:48:24 +02:00
|
|
|
keyword = LookupFull(token, sections);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (keyword < 0)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_INFO, "Unknown keyword \"%s\" in DEF file; ignoring.\n", token);
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (keyword)
|
|
|
|
|
{
|
|
|
|
|
case DEF_VERSION:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_NAMESCASESENSITIVE:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_TECHNOLOGY:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (strcmp(token, DBTechName))
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_WARNING, "DEF technology name \"%s\" does not"
|
2017-04-25 14:41:48 +02:00
|
|
|
" match current magic technology name \"%s\"\n",
|
|
|
|
|
token, DBTechName);
|
|
|
|
|
}
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_REGIONS:
|
|
|
|
|
LefSkipSection(f, sections[DEF_REGIONS]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_DESIGN:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
2025-08-26 20:48:27 +02:00
|
|
|
|
|
|
|
|
/* If rootDef is not NULL, then it was created before DESIGN
|
|
|
|
|
* was specified; this should not happen, but if so, then
|
|
|
|
|
* rename the rootDef rather than creating a new CellDef.
|
|
|
|
|
*/
|
|
|
|
|
if (rootDef == NULL)
|
|
|
|
|
rootDef = DefNewCell(token);
|
|
|
|
|
else
|
|
|
|
|
DBCellRenameDef(rootDef, token);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_UNITS:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &dscale) != 1)
|
|
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "Invalid syntax for UNITS statement.\n");
|
|
|
|
|
LefError(DEF_INFO, "Assuming default value of 100\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
dscale = 100;
|
|
|
|
|
}
|
|
|
|
|
/* We don't care if the scale is 100, 200, 1000, or 2000. */
|
|
|
|
|
/* Do we need to deal with numeric roundoff issues? */
|
|
|
|
|
oscale *= (float)dscale;
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_ROW:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_TRACKS:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_GCELLGRID:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_DIVIDERCHAR:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_BUSBITCHARS:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_HISTORY:
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_DIEAREA:
|
2022-10-15 17:33:59 +02:00
|
|
|
dierect = LefReadRect(f, 0, oscale);
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
|
|
|
|
|
2 * sizeof(int));
|
|
|
|
|
proprec->prop_type = PROPERTY_TYPE_DIMENSION;
|
|
|
|
|
proprec->prop_len = 4;
|
|
|
|
|
proprec->prop_value.prop_integer[0] = dierect->r_xbot;
|
|
|
|
|
proprec->prop_value.prop_integer[1] = dierect->r_ybot;
|
|
|
|
|
proprec->prop_value.prop_integer[2] = dierect->r_xtop;
|
|
|
|
|
proprec->prop_value.prop_integer[3] = dierect->r_ytop;
|
2025-08-26 20:48:27 +02:00
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
Extended the "property" command and modified the way that properties
are handled. Properties were previously only character strings,
which had become cumbersome because properties were being used for
mask hints and bounding boxes, with the necessity of constantly
converting values from string to integer and back, which can cause
a performance impact as well as just being messy. The main difference
to the command is the addition of an optional first keyword argument
for the property type, which can be "string", "integer", "dimension",
or "double". All types except "string" can consist of multiple
values. Multiple values can be specified as separate arguments on
the command line, so that, for example, values of FIXED_BBOX or
MASKHINTS_* no longer need to be quoted. In addition, this completes
the handling of "units" implemented recently, as all properties of
the type "dimension" can be entered in the current units, will display
in the current units, and will scale with the database.
2026-02-18 16:48:47 +01:00
|
|
|
DBPropPut(rootDef, "FIXED_BBOX", proprec);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
LefEndStatement(f);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_PROPERTYDEFINITIONS:
|
|
|
|
|
LefSkipSection(f, sections[DEF_PROPERTYDEFINITIONS]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_DEFAULTCAP:
|
|
|
|
|
LefSkipSection(f, sections[DEF_DEFAULTCAP]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_COMPONENTS:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &total) != 1) total = 0;
|
|
|
|
|
LefEndStatement(f);
|
2022-03-30 19:02:12 +02:00
|
|
|
if (annotate)
|
|
|
|
|
LefSkipSection(f, sections[DEF_COMPONENTS]);
|
|
|
|
|
else
|
2025-08-26 20:48:27 +02:00
|
|
|
{
|
|
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
2022-03-30 19:02:12 +02:00
|
|
|
DefReadComponents(f, rootDef, sections[DEF_COMPONENTS],
|
|
|
|
|
oscale, total);
|
2025-08-26 20:48:27 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEF_VIAS:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &total) != 1) total = 0;
|
|
|
|
|
LefEndStatement(f);
|
|
|
|
|
DefReadVias(f, sections[DEF_VIAS], oscale, total);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_PINS:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &total) != 1) total = 0;
|
|
|
|
|
LefEndStatement(f);
|
2025-08-26 20:48:27 +02:00
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
2022-09-14 19:02:55 +02:00
|
|
|
DefReadPins(f, rootDef, sections[DEF_PINS], oscale, total, annotate);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEF_PINPROPERTIES:
|
|
|
|
|
LefSkipSection(f, sections[DEF_PINPROPERTIES]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_SPECIALNETS:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &total) != 1) total = 0;
|
|
|
|
|
LefEndStatement(f);
|
2025-08-26 20:48:27 +02:00
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
2020-03-05 20:29:54 +01:00
|
|
|
DefReadNets(f, rootDef, sections[DEF_SPECIALNETS], oscale, TRUE,
|
2022-03-30 19:02:12 +02:00
|
|
|
dolabels, annotate, total);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEF_NETS:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &total) != 1) total = 0;
|
|
|
|
|
LefEndStatement(f);
|
2025-08-26 20:48:27 +02:00
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
2020-03-05 20:29:54 +01:00
|
|
|
DefReadNets(f, rootDef, sections[DEF_NETS], oscale, FALSE,
|
2022-03-30 19:02:12 +02:00
|
|
|
dolabels, annotate, total);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
2022-03-23 15:49:02 +01:00
|
|
|
case DEF_NONDEFAULTRULES:
|
|
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &total) != 1) total = 0;
|
|
|
|
|
LefEndStatement(f);
|
2025-08-26 20:48:27 +02:00
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
2022-03-23 15:49:02 +01:00
|
|
|
DefReadNonDefaultRules(f, rootDef, sections[DEF_NONDEFAULTRULES],
|
|
|
|
|
oscale, total);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEF_IOTIMINGS:
|
|
|
|
|
LefSkipSection(f, sections[DEF_IOTIMINGS]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_SCANCHAINS:
|
|
|
|
|
LefSkipSection(f, sections[DEF_SCANCHAINS]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_CONSTRAINTS:
|
|
|
|
|
LefSkipSection(f, sections[DEF_CONSTRAINTS]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_GROUPS:
|
|
|
|
|
LefSkipSection(f, sections[DEF_GROUPS]);
|
|
|
|
|
break;
|
|
|
|
|
case DEF_EXTENSION:
|
|
|
|
|
LefSkipSection(f, sections[DEF_EXTENSION]);
|
|
|
|
|
break;
|
2021-01-13 10:15:43 +01:00
|
|
|
case DEF_BLOCKAGES:
|
2022-03-24 22:58:05 +01:00
|
|
|
token = LefNextToken(f, TRUE);
|
|
|
|
|
if (sscanf(token, "%d", &total) != 1) total = 0;
|
|
|
|
|
LefEndStatement(f);
|
2022-06-24 21:22:53 +02:00
|
|
|
if (annotate || noblockage)
|
2022-03-30 19:02:12 +02:00
|
|
|
LefSkipSection(f, sections[DEF_BLOCKAGES]);
|
|
|
|
|
else
|
2025-08-26 20:48:27 +02:00
|
|
|
{
|
|
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
2022-03-30 19:02:12 +02:00
|
|
|
DefReadBlockages(f, rootDef, sections[DEF_BLOCKAGES],
|
|
|
|
|
oscale, total);
|
2025-08-26 20:48:27 +02:00
|
|
|
}
|
2021-01-13 10:15:43 +01:00
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEF_END:
|
2025-01-04 09:48:13 +01:00
|
|
|
if (!LefParseEndStatement(f, "DESIGN"))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_ERROR, "END statement out of context.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
keyword = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keyword == DEF_END) break;
|
|
|
|
|
}
|
|
|
|
|
TxPrintf("DEF read: Processed %d lines.\n", lefCurrentLine);
|
2019-06-06 15:59:56 +02:00
|
|
|
LefError(DEF_SUMMARY, NULL); /* print statement of errors, if any, and reset */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
|
|
2025-08-26 20:48:27 +02:00
|
|
|
if (rootDef != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBAdjustLabels(rootDef, &TiPlaneRect);
|
|
|
|
|
DBReComputeBbox(rootDef);
|
|
|
|
|
DBWAreaChanged(rootDef, &rootDef->cd_bbox, DBW_ALLWINDOWS,
|
2017-04-25 14:41:48 +02:00
|
|
|
&DBAllButSpaceBits);
|
2025-08-26 20:48:27 +02:00
|
|
|
DBCellSetModified(rootDef, TRUE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("DEF read: Design has no contents.\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (f != NULL) fclose(f);
|
|
|
|
|
UndoEnable();
|
2025-08-26 20:48:27 +02:00
|
|
|
return rootDef;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|