#ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResReadExt.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $"; #endif /* not lint */ /* *------------------------------------------------------------------------- * * ResReadExt.c -- Routines to parse .ext files for information needed * by extresist. * *------------------------------------------------------------------------- */ #include #include #include #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/geofast.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "utils/malloc.h" #include "textio/textio.h" #include "extract/extract.h" #include "extract/extractInt.h" #include "extflat/extflat.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "utils/utils.h" #include "utils/tech.h" #include "textio/txcommands.h" #include "resis/resis.h" /* constants defining where various fields can be found in .ext files. */ /* (FIXME---Needs to be changed to positions in .ext files) */ #define RDEV_LENGTH 4 #define RDEV_WIDTH 5 #define RDEV_DEVX 6 #define RDEV_DEVY 7 #define RDEV_ATTR 8 #define RDEV_NUM_ATTR 3 #define RESNODENAME 1 #define NODERESISTANCE 2 #define COUPLETERMINAL1 1 #define COUPLETERMINAL2 2 #define COUPLEVALUE 3 #define REALNAME 1 #define ALIASNAME 2 #define NODES_NODENAME 0 #define NODES_NODEX 1 #define NODES_NODEY 2 #define NODES_NODETYPE 3 #define NODE_BBOX_LL_X 5 #define NODE_BBOX_LL_Y 6 #define NODE_BBOX_UR_X 7 #define NODE_BBOX_UR_Y 8 #define NODELAMBDA 2 #define NODEUNITS 1 #define ATTRIBUTENODENAME 1 #define ATTRIBUTEVALUE 2 #define RES_EXT_ATTR 0 #define RES_EXT_ATTR_NAME 1 #define RES_EXT_ATTR_X 2 #define RES_EXT_ATTR_Y 3 #define RES_EXT_ATTR_TILE 6 #define RES_EXT_ATTR_TEXT 7 #define MAXTOKEN 1024 #define MAXLINE 40 #define MAXDIGIT 20 ResExtNode *ResInitializeNode(); ResExtNode *ResOriginalNodes; /*Linked List of Nodes */ static float resscale = 1.0; /* Scale factor */ char RDEV_NOATTR[1] = {'0'}; ResFixPoint *ResFixList; /* *------------------------------------------------------------------------- * * ResReadExt-- * * Results: returns 0 if ext file is correct, 1 if not. * * Side Effects:Reads in ExtTable and makes a hash table of nodes. * *------------------------------------------------------------------------- */ int ResReadExt(char *extfile); { char *line = NULL, *argv[128]; int result, fettype, readdrivepoints; int size = 0; FILE *fp; fp = PaOpen(extfile, "r", ".ext", EFSearchPath, EFLibPath, (char **)NULL); if (fp == NULL) { TxError("Cannot open file %s%s\n", extfile, ".ext"); return 1; } readdrivepoints = FALSE; /* Read in the file. Makes use of various functions * from extflat, mostly in EFread.c. */ EFSaveLocs = False; efReadLineNum = 0; while ((argc = efReadLine(&line, &size, fp, argv)) >= 0) { n = LookupStruct(argv[0], (const LookupTable *)keyTable, sizeof keyTable[0]); if (n < 0) { efReadError("Unrecognized token \"%s\" (ignored)\n", argv[0]); continue; } if (argc < keyTable[n].k_mintokens) { efReadError("Not enough tokens for %s line\n", argv[0]); continue; } /* We don't care about most tokens, only DEVICE, NODE, PORT, * and SUBSTRATE; and MERGE is used to locate drive points. */ switch (keyTable[n].k_key) { case SCALE: resscale = (float)atof(argv[1]); if (resscale == 0.0) resscale = 1.0; break; case DEVICE: ResReadSubckt(argc, argv); break; case FET: ResReadDevice(argc, argv); break; case MERGE: ResReadDrivePoint(argc, argv); break; case NODE: case SUBSTRATE: ResReadNode(argc, argv); break; case PORT: ResReadPort(argc, argv); break; case ATTR: result = ResExtAttribute(curnodename, argv[1], &readdrivepoints); break; default: break; /* to do: Handle CAP, RESISTOR, maybe others? */ } } fclose(fp); return(result); } /* *------------------------------------------------------------------------- * * ResReadNode-- Reads in a node file, puts location of nodes into node * structures. * * Results: 0 if the node was read correctly, 1 otherwise. * * Side Effects: see above * *------------------------------------------------------------------------- */ int ResReadNode(int argc, char *argv[]) { HashEntry *entry; ResExtNode *node; char *cp; float lambda; entry = HashFind(&ResNodeTable, line[NODES_NODENAME]); node = ResInitializeNode(entry); node->location.p_x = atoi(line[NODES_NODEX]); node->location.p_y = atoi(line[NODES_NODEY]); if ((cp = strchr(line[NODES_NODETYPE], ';'))) *cp = '\0'; node->type = DBTechNameType(line[NODES_NODETYPE]); if (node->type == -1) { TxError("Bad tile type name in .ext file for node %s\n", node->name); return 1; } return 0; } /* *------------------------------------------------------------------------- * * ResReadSubckt-- Processes a subcircuit line from a ext file. * This uses the "user subcircuit" extension defined in * IRSIM, although it is mostly intended as a way to work * around the device type limitations of the .ext format * when using extresist. * * Results: returns 0 if line was added correctly. * * Side Effects: Allocates devices and adds nodes to the node hash table. * *------------------------------------------------------------------------- */ int ResReadSubckt(line) char line[][MAXTOKEN]; { RDev *device; int rvalue, i, j, k; static int nowarning = TRUE; float lambda; TileType ttype = TT_SPACE; char *lptr = NULL, *wptr = NULL; ExtDevice *devptr; device = (RDev *) mallocMagic((unsigned) (sizeof(RDev))); device->status = FALSE; device->nextDev = ResRDevList; lambda = (float)ExtCurStyle->exts_unitsPerLambda / resscale; device->location.p_x = 0; device->location.p_y = 0; device->rs_gattr=RDEV_NOATTR; device->rs_sattr=RDEV_NOATTR; device->rs_dattr=RDEV_NOATTR; ResRDevList = device; device->layout = NULL; device->source = device->drain = device->gate = device->subs = NULL; /* The last argument is the name of the device */ for (i = 1; line[i][0] != '\0'; i++); i--; /* To do: Replace this search with a pre-prepared hash */ /* table to key off of the device name. */ for (j = 0; j < EFDevNumTypes; j++) if (!strcmp(EFDevTypes[j], line[i])) break; /* Read attributes, especially to pick up values for L, W, X, and Y; * and source and drain area and perimeter, that are critical for use * by extresist. */ for (k = 1; line[k][0] != '\0'; k++) { char *eqptr; eqptr = strchr(line[k], '='); if (eqptr != NULL) { if (k < i) i = k; eqptr++; switch (line[k][0]) { case 'l': lptr = eqptr; break; case 'w': wptr = eqptr; break; case 'x': device->location.p_x = (int)((float)atof(eqptr) / lambda); break; case 'y': device->location.p_y = (int)((float)atof(eqptr) / lambda); break; case 's': device->rs_sattr = StrDup((char **)NULL, eqptr); break; case 'd': device->rs_dattr = StrDup((char **)NULL, eqptr); break; } } } if (j == EFDevNumTypes) { TxError("Failure to find device type %s\n", line[i]); return 1; } ttype = extGetDevType(EFDevTypes[j]); ASSERT(ttype >= 0, "ttype<0"); /* Find the device record that corresponds to the device name */ for (devptr = ExtCurStyle->exts_device[ttype]; devptr; devptr = devptr->exts_next) if (!strcmp(devptr->exts_deviceName, EFDevTypes[j])) break; device->rs_devptr = devptr; device->rs_ttype = ttype; if (lptr != NULL && wptr != NULL) { HashEntry *he; float rpersquare; he = HashLookOnly(&devptr->exts_deviceResist, "linear"); if (he != NULL) rpersquare = (ResValue)(spointertype)HashGetValue(he); else rpersquare = (ResValue)0.0; /* Subcircuit types may not have a length or width value, in which */ /* case it is zero. Don't induce a divide-by-zero error. */ if (MagAtof(wptr) == 0) device->resistance = 0; else device->resistance = MagAtof(lptr) * rpersquare/MagAtof(wptr); } else device->resistance = 0; rvalue = 0; for (k = 1; k < i; k++) { if (k > SUBS) { TxError("Device %s has more than 4 ports (not handled).\n", line[i]); break; /* No method to handle more ports than this */ } rvalue += ResExtNewNode(line[k], k, device); } return rvalue; } /* *------------------------------------------------------------------------- * * ResReadDevice-- Processes a device line from a ext file. * * Results: returns 0 if line was added correctly. * * Side Effects: Allocates devices and adds nodes to the node hash table. * *------------------------------------------------------------------------- */ int ResReadDevice(line, rpersquare, devptr) char line[][MAXTOKEN]; float rpersquare; ExtDevice *devptr; { RDev *device; int rvalue, i, j, k; char *newattr, tmpattr[MAXTOKEN]; static int nowarning = TRUE; float lambda; ExtDevice *devtest; if ((line[RDEV_WIDTH][0] == '\0') || (line[RDEV_LENGTH][0] == '\0')) { TxError("error in input file:\n"); return 1; } device = (RDev *)mallocMagic((unsigned)(sizeof(RDev))); if (nowarning && rpersquare == 0) { TxError("Warning: FET resistance not included or " "set to zero in technology file-\n"); TxError("All driven nodes will be extracted\n"); nowarning = FALSE; } if (MagAtof(line[RDEV_WIDTH]) != 0) device->resistance = MagAtof(line[RDEV_LENGTH]) * rpersquare / MagAtof(line[RDEV_WIDTH]); else device->resistance = 0; device->status = FALSE; device->nextDev = ResRDevList; /* Check that devptr matches the device name and number of terminals */ /* Note that this routine is only called for the original "fet" */ /* types with fixed names, so the names must match and there must */ /* always be three terminals (two source/drain terminals). */ if (devptr->exts_deviceSDCount != 2) for (devtest = devptr->exts_next; devtest; devtest = devtest->exts_next) if (devtest->exts_deviceSDCount == 2) { devptr = devtest; break; } lambda = (float)ExtCurStyle->exts_unitsPerLambda / resscale; device->location.p_x = (int)((float)atof(line[RDEV_DEVX]) / lambda); device->location.p_y = (int)((float)atof(line[RDEV_DEVY]) / lambda); device->rs_gattr=RDEV_NOATTR; device->rs_sattr=RDEV_NOATTR; device->rs_dattr=RDEV_NOATTR; device->rs_devptr = devptr; device->gate = device->source = device->drain = device->subs = NULL; device->rs_ttype = extGetDevType(devptr->exts_deviceName); /* ext attributes look like g=a1,a2 */ /* ext attributes are "a1","a2" */ /* Do conversion from one to the other here */ /* NOTE: As of version 8.3.366, .ext attributes will end in two */ /* integer values, not quoted, for device area and perimeter. Do */ /* not quote them. */ for (i = RDEV_ATTR; i < RDEV_ATTR + RDEV_NUM_ATTR; i++) { char *cptr, *sptr; int d1, d2; if (line[i][0] == '\0') break; sptr = &line[i][2]; /* Start after "s=" or "d=" */ tmpattr[0] = '\0'; while ((cptr = strchr(sptr, ',')) != NULL) { if (sscanf(sptr, "%d,%d", &d1, &d2) == 2) { strcat(tmpattr, sptr); sptr = NULL; break; } else { *cptr = '\0'; strcat(tmpattr, "\""); strcat(tmpattr, sptr); strcat(tmpattr, "\","); sptr = cptr + 1; *cptr = ','; } } if (sptr && (strlen(sptr) != 0)) { strcat(tmpattr, "\""); strcat(tmpattr, sptr); strcat(tmpattr, "\""); } newattr = (char *)mallocMagic(strlen(tmpattr) + 1); strcpy(newattr, tmpattr); switch (line[i][0]) { case 'g': device->rs_gattr = newattr; break; case 's': device->rs_sattr = newattr; break; case 'd': device->rs_dattr = newattr; break; default: TxError("Bad fet attribute\n"); break; } } ResRDevList = device; device->layout = NULL; rvalue = ResExtNewNode(line[GATE], GATE, device) + ResExtNewNode(line[SOURCE], SOURCE, device) + ResExtNewNode(line[DRAIN], DRAIN, device); return rvalue; } /* *------------------------------------------------------------------------- * * ResExtNewNode-- Adds a new node to the Node Hash Table. * * Results: returns zero if node is added correctly, one otherwise. * * Side Effects: Allocates a new ResExtNode * *------------------------------------------------------------------------- */ int ResExtNewNode(line, type, device) char line[]; int type; RDev *device; { HashEntry *entry; ResExtNode *node; devPtr *tptr; if (line[0] == '\0') { TxError("Missing device connection\n"); return 1; } entry = HashFind(&ResNodeTable, line); node = ResInitializeNode(entry); tptr = (devPtr *)mallocMagic((unsigned)(sizeof(devPtr))); tptr->thisDev = device; tptr->nextDev = node->firstDev; node->firstDev = tptr; tptr->terminal = type; switch(type) { case GATE: device->gate = node; break; case SOURCE: device->source = node; break; case DRAIN: device->drain = node; break; case SUBS: device->subs = node; break; default: TxError("Bad Terminal Specifier\n"); break; } return 0; } /* *------------------------------------------------------------------------- * * ResReadCapacitor-- Adds the capacitance from a C line to the appropriate * node. Coupling capacitors are added twice, moving the capacitance * to the substrate. * * Results: * Always return 0 * * Side Effects: modifies capacitance field of ResExtNode. * *------------------------------------------------------------------------- */ int ResReadCapacitor(line) char line[][MAXTOKEN]; { HashEntry *entry1, *entry2; ResExtNode *node1, *node2; if (line[COUPLETERMINAL1][0] == 0 || line[COUPLETERMINAL2][0] == 0) { TxError("Bad Capacitor\n"); return(1); } entry1 = HashFind(&ResNodeTable, line[COUPLETERMINAL1]); node1 = ResInitializeNode(entry1); if (ResOptionsFlags & ResOpt_Signal) { node1->capacitance += MagAtof(line[COUPLEVALUE]); if (strcmp(line[COUPLETERMINAL2], "GND") == 0 || strcmp(line[COUPLETERMINAL2], "Vdd") == 0) { return 0; } entry2 = HashFind(&ResNodeTable, line[COUPLETERMINAL2]); node2 = ResInitializeNode(entry2); node2->capacitance += MagAtof(line[COUPLEVALUE]); return 0; } if (strcmp(line[COUPLETERMINAL2], "GND") == 0) { node1->capacitance += MagAtof(line[COUPLEVALUE]); return 0; } if (strcmp(line[COUPLETERMINAL2], "Vdd") == 0) { node1->cap_vdd += MagAtof(line[COUPLEVALUE]); return 0; } entry2 = HashFind(&ResNodeTable, line[COUPLETERMINAL2]); node2 = ResInitializeNode(entry2); if (strcmp(line[COUPLETERMINAL1], "GND") == 0) { node2->capacitance += MagAtof(line[COUPLEVALUE]); return 0; } if (strcmp(line[COUPLETERMINAL1], "Vdd") == 0) { node2->cap_vdd += MagAtof(line[COUPLEVALUE]); return 0; } node1->cap_couple += MagAtof(line[COUPLEVALUE]); node2->cap_couple += MagAtof(line[COUPLEVALUE]); return 0; } /* *------------------------------------------------------------------------- * * ResReadResistor-- Adds the capacitance from a R line to the appropriate * node. * * Results * Return 0 to keep search going, 1 to abort * * Side Effects: modifies resistance field of ResExtNode * *------------------------------------------------------------------------- */ int ResReadResistor(line) char line[][MAXTOKEN]; { HashEntry *entry; ResExtNode *node; if (line[RESNODENAME][0] == 0) { TxError("Bad Resistor\n"); return 1; } entry = HashFind(&ResNodeTable, line[RESNODENAME]); node = ResInitializeNode(entry); if (node->resistance != 0) { TxError("Duplicate Resistance Entries\n"); return 1; } node->resistance = MagAtof(line[NODERESISTANCE]); return(0); } /* *------------------------------------------------------------------------- * * ResExtAttribute--checks to see if a node attribute is a resistance * attribute. If it is, add it to the correct node's status flag. * Only works with 5.0 1/line attributes * * Results: * Return 0 to keep search going, 1 to abort * * Side Effects: modifies resistance field of ResExtNode * *------------------------------------------------------------------------- */ int ResReadAttribute(aname, avalue, readdrivepoints) char *aname; char *avalue; bool *readdrivepoints; { HashEntry *entry; ResExtNode *node; char digit[MAXDIGIT]; int i; static int notwarned = TRUE; if (aname[0] == 0) { TxError("Bad Resistor\n"); return 1; } entry = HashFind(&ResNodeTable, aname); node = ResInitializeNode(entry); if (strncmp(avalue, "res:skip", 8) == 0) { if (node->status & FORCE) { TxError("Warning: Node %s is both forced and skipped\n", aname); } else { node->status |= SKIP; } } else if (strncmp(avalue, "res:force", 9) == 0) { if (node->status & SKIP) TxError("Warning: Node %s is both skipped and forced \n", aname); else node->status |= FORCE; } else if (strncmp(avalue, "res:min=", 8) == 0) { node->status |= MINSIZE; for (i = 0, avalue += 8; *avalue != '\0'; avalue++) { digit[i++] = *avalue; } digit[i++] = '\0'; node->minsizeres = MagAtof(digit); } else if (strncmp(avalue, "res:drive", 9) == 0 && (ResOptionsFlags & ResOpt_Signal)) { if (*readdrivepoints == FALSE) { ResExtProcessDrivePoints(rootname); *readddrivepoints = TRUE; } /* is the attribute in root.ext? */ if (node->drivepoint.p_x != INFINITY) node->status |= DRIVELOC; else { if (notwarned) TxError("Drivepoint for %s not defined in %s.ext; is it " "defined in a child cell?\n", node->name, rootname); notwarned = FALSE; } } return 0; } /* *------------------------------------------------------------------------- * * ResExtProcessDrivePoints -- if the ext file contains a res:drive attribute, * and we are doing a signal extraction, * we need to search through the .ext file looking for attr labels that * contain this text. For efficiency, the .ext file is only parsed when * the first res:drive is encountered. res:drive labels only work if * they are in the root cell. * * FIXME---The .ext file is being read and this routine should only * read in additional lines as necessary. * * Results: * None. * * Side Effects: * *------------------------------------------------------------------------- */ void ResExtProcessDrivePoints(FILE *fp) { char line[MAXLINE][MAXTOKEN]; FILE *fp; HashEntry *entry; ResExtNode *node; while (gettokens(line, fp) != 0) { if (strncmp(line[RES_EXT_ATTR], "attr", 4) != 0 || strncmp(line[RES_EXT_ATTR_TEXT], "\"res:drive\"", 11) != 0) continue; entry = HashFind(&ResNodeTable, line[RES_EXT_ATTR_NAME]); node = ResInitializeNode(entry); node->drivepoint.p_x = atoi(line[RES_EXT_ATTR_X]); node->drivepoint.p_y = atoi(line[RES_EXT_ATTR_Y]); node->rs_ttype = DBTechNoisyNameType(line[RES_EXT_ATTR_TILE]); } fclose(fp); } /* *------------------------------------------------------------------------- * * ResExtProcessFixPoints -- if the ext file contains a "res:fix:name" label * and we are checking for power supply noise, then we have to * parse the .ext file looking for the fix label locations. This * is only done after the first res:fix label is encountered. * * (FIXME---This is now all in the .ext file so the information does * not need to be read twice.) * * Results: * None. * * Side Effects: * For each new name, allocate memory * *------------------------------------------------------------------------- */ void ResExtProcessFixPoints(filename) char *filename; { char line[MAXLINE][MAXTOKEN], *label, *c; FILE *fp; ResFixPoint *thisfix; fp = PaOpen(filename, "r", ".ext", (ExtLocalPath == NULL) ? "." : ExtLocalPath, (char *)NULL, (char **)NULL); if (fp == NULL) { TxError("Cannot open file %s%s\n", filename, ".ext"); return; } while (gettokens(line, fp) != 0) { if (strncmp(line[RES_EXT_ATTR], "attr", 4) != 0 || strncmp(line[RES_EXT_ATTR_TEXT], "\"res:fix", 8) != 0) continue; label = line[RES_EXT_ATTR_TEXT]; label += 8; if (*label == ':') label++; if ((c=strrchr(label, '"')) != NULL) *c = '\0'; else if (*label != '\0') { TxError("Bad res:fix attribute label %s\n", line[RES_EXT_ATTR_TEXT]); *label ='\0'; } thisfix = (ResFixPoint *)mallocMagic((unsigned)(sizeof(ResFixPoint) + strlen(label))); thisfix->fp_next = ResFixList; ResFixList = thisfix; thisfix->fp_loc.p_x = atoi(line[RES_EXT_ATTR_X]); thisfix->fp_loc.p_y = atoi(line[RES_EXT_ATTR_Y]); thisfix->fp_ttype = DBTechNoisyNameType(line[RES_EXT_ATTR_TILE]); thisfix->fp_tile = NULL; strcpy(thisfix->fp_name, label); } fclose(fp); } /* *------------------------------------------------------------------------- * * ResInitializeNode -- * Gets the node corresponding to a given hash table entry. If no * such node exists, one is created. * * Results: Returns ResExtNode corresponding to entry. * * Side Effects: May allocate a new ResExtNode. * *------------------------------------------------------------------------- */ ResExtNode * ResInitializeNode(entry) HashEntry *entry; { ResExtNode *node; if ((node = (ResExtNode *) HashGetValue(entry)) == NULL) { node = (ResExtNode *)mallocMagic((unsigned)(sizeof(ResExtNode))); HashSetValue(entry, (char *) node); node->nextnode = ResOriginalNodes; ResOriginalNodes = node; node->status = FALSE; node->forward = (ResExtNode *) NULL; node->capacitance = 0; node->cap_vdd = 0; node->cap_couple = 0; node->resistance = 0; node->type = 0; node->firstDev = NULL; node->name = entry->h_key.h_name; node->oldname = NULL; node->drivepoint.p_x = INFINITY; node->drivepoint.p_y = INFINITY; node->location.p_x = INFINITY; node->location.p_y = INFINITY; node->rs_sublist[0] = NULL; node->rs_sublist[1] = NULL; } while (node->status & FORWARD) { node = node->forward; } return node; }