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 */
|
|
|
|
|
|
|
|
|
|
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) */
|
|
|
|
|
freeMagic((char *)routeTop);
|
|
|
|
|
routeTop = routeTop->r_next;
|
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
char *propval;
|
|
|
|
|
bool found;
|
|
|
|
|
|
2024-10-02 07:59:00 +02:00
|
|
|
propval = (char *)DBPropGet(use->cu_def, "FIXED_BBOX", &found);
|
2019-03-23 00:58:47 +01:00
|
|
|
if (found)
|
|
|
|
|
{
|
|
|
|
|
if (sscanf(propval, "%d %d %d %d", &rect.r_xbot, &rect.r_ybot,
|
|
|
|
|
&rect.r_xtop, &rect.r_ytop) == 4)
|
|
|
|
|
r = ▭
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
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;
|
|
|
|
|
|
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);
|
2021-09-15 20:45:14 +02:00
|
|
|
freeMagic(rectList);
|
|
|
|
|
rectList = rectList->r_next;
|
|
|
|
|
}
|
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;
|
|
|
|
|
|
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);
|
2021-09-15 20:45:14 +02:00
|
|
|
freeMagic(rectList);
|
|
|
|
|
rectList = rectList->r_next;
|
|
|
|
|
}
|
|
|
|
|
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;
|
2017-04-25 14:41:48 +02:00
|
|
|
char usename[512];
|
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);
|
|
|
|
|
if (sscanf(token, "%511s", usename) != 1)
|
|
|
|
|
{
|
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
|
|
|
|
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 */
|
|
|
|
|
|
|
|
|
|
if ((defMacro == NULL) || ((defUse = DBCellNewUse(defMacro, usename))
|
2022-03-30 19:02:12 +02:00
|
|
|
== NULL))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2018-01-30 16:01:58 +01:00
|
|
|
if (defMacro != NULL) 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;
|
2022-10-15 17:33:59 +02:00
|
|
|
char *bboxstr;
|
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);
|
|
|
|
|
bboxstr = mallocMagic(40);
|
|
|
|
|
sprintf(bboxstr, "%d %d %d %d",
|
|
|
|
|
dierect->r_xbot,
|
|
|
|
|
dierect->r_ybot,
|
|
|
|
|
dierect->r_xtop,
|
|
|
|
|
dierect->r_ytop);
|
2025-08-26 20:48:27 +02:00
|
|
|
if (rootDef == NULL) rootDef = DefNewCell(inName);
|
2022-10-15 17:33:59 +02:00
|
|
|
DBPropPut(rootDef, "FIXED_BBOX", bboxstr);
|
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
|
|
|
}
|