Logo Search packages:      
Sourcecode: magic version File versions  Download package

lefRead.c

/*
 * lefRead.c --      
 *
 * This module incorporates the LEF/DEF format for standard-cell place and
 * route.
 *
 * Version 0.1 (September 26, 2003):  LEF input handling.  Includes creation
 * of cells from macro statements, handling of pins, ports, obstructions, and
 * associated geometry.
 *
 * Note that techfile compatibility requires that each layer name appearing
 * in the LEF file should be present as an alias for the appropriate magic
 * tile type in the technology file.  Layer names are not case sensitive.
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-7.5/lef/lefRead.c,v 1.11 2006/11/02 20:00:07 tim Exp $";            
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/time.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"
#include "database/database.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "dbwind/dbwind.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "cif/cif.h"
#include "cif/CIFint.h"       /* Access to CIFCurStyle. . . */
#include "lef/lefInt.h"

/* ---------------------------------------------------------------------*/

/* Current line number for reading */
int lefCurrentLine;

/* Cell reading hash tables */
HashTable LefCellTable;
HashTable lefDefInitHash;

/*
 *------------------------------------------------------------
 *
 * LefEstimate --
 *
 *    Estimate the time to completion based on the time,
 *    the total number of items to process, and the number
 *    of items processed so far.  Attempts to report about
 *    every 5 seconds or so.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    May print to output.
 *
 *------------------------------------------------------------
 */

#define PRINT_INTERVAL 5      /* print status at 4 second intervals */

void
LefEstimate(processed, total, item_name)
   int processed;
   int total;
   char *item_name;
{
    static int check_interval, partition;
    static struct timeval tv_start;
    static float last_time;
    struct timeval tv;
    struct timezone tz;
    float cur_time, time_left;

    if (!total) return;

    if (processed == 0)       /* Initialization */
    {
      gettimeofday(&tv_start, &tz);
      GrDisplayStatus = DISPLAY_IN_PROGRESS;
      SigSetTimer(PRINT_INTERVAL);
    }
    else if (processed == total - 1)
    {
      GrDisplayStatus = DISPLAY_IDLE;
      SigRemoveTimer();
    }
    else if (GrDisplayStatus == DISPLAY_BREAK_PENDING)
    {
      gettimeofday(&tv, &tz);
      cur_time = (float)(tv.tv_sec - tv_start.tv_sec)
            + ((float)(tv.tv_usec - tv_start.tv_usec) / 1.0e6);
      time_left = (((float)total / (float)processed) - 1) * cur_time;

      /* not likely to happen, but we don't want a divide-by-0 error */
      if (cur_time == 0.0) cur_time = 1.0e-6;

      TxPrintf("  Processed %d of %d %s (%2.1f%%).", processed, total,
                  item_name, (float)(100 * processed) / (float)total);
      TxPrintf("  Est. time remaining: %2.1fs\n", time_left);
      TxFlushOut();

#ifdef MAGIC_WRAPPER
      /* We need to let Tk paint the console display */
      while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0);
#endif

      GrDisplayStatus = DISPLAY_IN_PROGRESS;
      SigSetTimer(PRINT_INTERVAL);
    }
}

/* This is the original version, which doesn't use the system itimer    */
/* and which is not very good at maintaining constant intervals.  */

#if 0
void
LefEstimate(processed, total, item_name)
   int processed;
   int total;
   char *item_name;
{
    static int check_interval, partition;
    static struct timeval tv_start;
    static float last_time;
    struct timeval tv;
    struct timezone tz;
    float cur_time, time_left;

    if (!total) return;

    if (processed == 0)       /* Initialization */
    {
      GrDisplayStatus = DISPLAY_IN_PROGRESS;

      check_interval = 100;
      gettimeofday(&tv_start, &tz);
      last_time = 0.0;
    }
      
    if (processed > check_interval)
    {
      gettimeofday(&tv, &tz);
      cur_time = (float)(tv.tv_sec - tv_start.tv_sec)
            + ((float)(tv.tv_usec - tv_start.tv_usec) / 1.0e6);
      time_left = (((float)total / (float)processed) - 1) * cur_time;

      /* not likely to happen, but we don't want a divide-by-0 error */
      if (cur_time == 0.0) cur_time = 1.0e-6;

      partition = (int)((float)processed * (float)PRINT_INTERVAL / cur_time);

      /* partition is never less than 1 nor greater than 5% of the total */
      if (partition == 0) partition = 1;
      if (partition > (total / 20)) partition = (total / 20);

      check_interval += partition;

      /* Don't print anything in intervals faster than 1 second */
      if ((cur_time - last_time) < 1.0) return;
      last_time = cur_time;

      TxPrintf("  Processed %d of %d %s (%2.1f%%).", processed, total,
                  item_name, (float)(100 * processed) / (float)total);
      TxPrintf("  Est. time remaining: %2.1fs\n", time_left);
      TxFlushOut();
    }
}

#endif /* 0 */


/*
 *------------------------------------------------------------
 *
 * LefNextToken --
 *
 *    Move to the next token in the stream input.
 *    If "ignore_eol" is FALSE, then the end-of-line character
 *    "\n" will be returned as a token when encountered.
 *    Otherwise, end-of-line will be ignored.
 *
 * Results:
 *    Pointer to next token to parse
 *
 * Side Effects:
 *    May read a new line from the specified file.
 *
 * Warnings:
 *    The return result of LefNextToken will be overwritten by
 *    subsequent calls to LefNextToken if more than one line of
 *    input is parsed.
 *
 *------------------------------------------------------------
 */

char *
LefNextToken(f, ignore_eol)
    FILE *f;
    bool ignore_eol;
{
    static char line[LEF_LINE_MAX + 2];   /* input buffer */
    static char *nexttoken = NULL;  /* pointer to next token */
    static char *curtoken;          /* pointer to current token */
    static char eol_token='\n';

    /* Read a new line if necessary */

    if (nexttoken == NULL)
    {
      for(;;)
      {
          if (fgets(line, LEF_LINE_MAX + 1, f) == NULL) return NULL;
          lefCurrentLine++;
          curtoken = line;
          while (isspace(*curtoken) && (*curtoken != '\n') && (*curtoken != '\0'))
            curtoken++;       /* skip leading whitespace */

          if ((*curtoken != '#') && (*curtoken != '\n') && (*curtoken != '\0'))
          {
            nexttoken = curtoken;
            break;
          }
      }
      if (!ignore_eol)
          return &eol_token;
    }
    else
      curtoken = nexttoken;

    /* Find the next token; set to NULL if none (end-of-line). */

    while (!isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n'))
      nexttoken++;      /* skip non-whitespace (move past current token) */

    /* Terminate the current token */
    if (*nexttoken != '\0') *nexttoken++ = '\0';

    while (isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n'))
      nexttoken++;      /* skip any whitespace */

    if ((*nexttoken == '#') || (*nexttoken == '\n') || (*nexttoken == '\0'))
      nexttoken = NULL;

    return curtoken;
}

/*
 *------------------------------------------------------------
 *
 * LefError --
 *
 *    Print an error message (via TxError) giving the line
 *    number of the input file on which the error occurred.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Prints to the output (stderr).
 *
 *------------------------------------------------------------
 */

void
LefError(char *fmt, ...)
{  
    static int errors = 0;
    va_list args;

    if (fmt == NULL)  /* Special case:  report any errors and reset */
    {
      if (errors)
      {
          TxPrintf("LEF Read: encountered %d error%s total.\n", errors,
                  (errors == 1) ? "" : "s");
          errors = 0;
      }
      return;
    }

    if (errors < LEF_MAX_ERRORS)
    {
      TxError("LEF Read, Line %d: ", lefCurrentLine);
      va_start(args, fmt);
      Vfprintf(stderr, fmt, args);
      va_end(args);
      TxFlushErr();
    }
    else if (errors == LEF_MAX_ERRORS)
      TxError("LEF Read:  Further errors will not be reported.\n");

    errors++;
}

/*
 *------------------------------------------------------------
 *
 * LefParseEndStatement --
 *
 *    Check if the string passed in "lineptr" contains the
 *    appropriate matching keyword.  Sections in LEF files
 *    should end with "END (keyword)" or "END".  To check
 *    against the latter case, "match" should be NULL.
 *
 * Results:
 *    TRUE if the line matches the expected end statement,
 *    FALSE if not. 
 *
 * Side effects:
 *    None.
 *
 *------------------------------------------------------------
 */

bool
LefParseEndStatement(f, match)
    FILE *f;
    char *match;
{
    char *token;
    int keyword, words;
    char *match_name[2];

    match_name[0] = match;
    match_name[1] = NULL;

    token = LefNextToken(f, (match == NULL) ? FALSE : TRUE);
    if (token == NULL)
    {
      LefError("Bad file read while looking for END statement\n");
      return FALSE;
    }

    /* END or ENDEXT */
    if ((*token == '\n') && (match == NULL)) return TRUE;

    /* END <section_name> */
    else {
      keyword = LookupFull(token, match_name);
      if (keyword == 0)
          return TRUE;
      else
          return FALSE;
    }
}

/*
 *------------------------------------------------------------
 *
 * LefSkipSection --
 *
 *    Skip to the "END" record of a LEF input section
 *    String "section" must follow the "END" statement in
 *    the file to be considered a match;  however, if
 *    section = NULL, then "END" must have no strings
 *    following.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Reads input from the specified file.  Prints an
 *    error message if the expected END record cannot
 *    be found.
 *
 *------------------------------------------------------------
 */

void
LefSkipSection(f, section)
    FILE *f;
    char *section;
{
    char *token;
    int keyword;
    static char *end_section[] = {
      "END",
      "ENDEXT",
      NULL
    };

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
      if ((keyword = Lookup(token, end_section)) == 0)
      {
          if (LefParseEndStatement(f, section))
            return;
      }
      else if (keyword == 1)
      {
          if (!strcmp(section, "BEGINEXT"))
            return;
      }
    }

    LefError("Section %s has no END record!\n", section);
    return;
}

/*
 *------------------------------------------------------------
 *
 * lefFindCell --
 *
 *------------------------------------------------------------
 */

CellDef *
lefFindCell(name)
    char *name;         /* Name of the cell to search for */
{
    HashEntry *h;
    CellDef *def;

    h = HashFind(&LefCellTable, name);
    if (HashGetValue(h) == 0)
    {
      def = DBCellLookDef(name);
      if (def == NULL)
      {
          def = DBCellNewDef(name, (char *)NULL);
          DBReComputeBbox(def);
      }
      HashSetValue(h, def);
    }
    return (CellDef *) HashGetValue(h);
}

/*
 *------------------------------------------------------------
 *
 * LefLower --
 *
 *    Convert a token in a LEF or DEF file to all-lowercase.
 *
 *------------------------------------------------------------
 */

char *
LefLower(token)
    char *token;
{
    char *tptr;

    for (tptr = token; *tptr != '\0'; tptr++)
      *tptr = tolower(*tptr);

    return token;
}

/*
 *------------------------------------------------------------
 * LefRedefined --
 *
 *    In preparation for redefining a LEF layer, we need
 *    to first check if there are multiple names associated
 *    with the lefLayer entry.  If so, split the entry into
 *    two copies, so that the redefinition doesn't affect
 *    the other LEF names.
 *
 * Results:
 *    Pointer to a lefLayer, which may or may not be the
 *    same one presented to the subroutine.
 *    
 * Side Effects:
 *    May add an entry to the list of LEF layers.
 *
 *------------------------------------------------------------
 */

lefLayer *
LefRedefined(lefl, redefname)
    lefLayer *lefl;
    char *redefname;
{
    lefLayer *slef, *newlefl;
    char *altName;
    linkedRect *viaLR;
    HashSearch hs;
    HashEntry *he;
    int records;

    /* check if more than one entry points to the same      */
    /* lefLayer record.  If so, we will also record the     */
    /* name of the first type that is not the same as */
    /* "redefname".                             */

    records = 0;
    altName = NULL;
    HashStartSearch(&hs);
    while (he = HashNext(&LefInfo, &hs))
    {
      slef = (lefLayer *)HashGetValue(he);
      if (slef == lefl)
          records++;
      if (altName == NULL)
          if (strcmp((char *)he->h_key.h_name, redefname))
            altName = (char *)he->h_key.h_name;
    }
    if (records == 1)
    {
      /* Only one name associated with the record, so */
      /* just clear all the allocated information.    */

      for (viaLR = lefl->info.via.lr; viaLR != NULL; viaLR = viaLR->rect_next)
          freeMagic(viaLR);
      newlefl = lefl;
    }
    else
    {
      he = HashFind(&LefInfo, redefname);
      newlefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
      newlefl->refCnt = 1;
      newlefl->canonName = (char *)he->h_key.h_name;
      HashSetValue(he, newlefl);

      /* If the canonical name of the original entry  */
      /* is "redefname", then change it.        */

      if (!strcmp(lefl->canonName, redefname))
          if (altName != NULL)
            lefl->canonName = altName;
    }
    newlefl->type = -1;
    newlefl->obsType = -1;
    newlefl->info.via.area = GeoNullRect;
    newlefl->info.via.cell = (CellDef *)NULL;
    newlefl->info.via.lr = (linkedRect *)NULL;

    return newlefl;
}

/*
 *------------------------------------------------------------
 * LefReadLayers --
 *
 *    Read a LEF "LAYER" record from the file, and
 *    match the LEF layer to a magic tile type.
 *    If "obstruct" is TRUE, returns the layer mapping
 *    for obstruction geometry as defined in the
 *    technology file (if it exists), and up to two
 *    types are returned (the second in the 3rd argument
 *    pointer).
 *
 * Results:
 *    Returns a magic TileType or -1 if no matching
 *    tile type is found.
 *
 * Side Effects:
 *    Reads input from file f;
 *
 *------------------------------------------------------------
 */

TileType
LefReadLayers(f, obstruct, lreturn)
    FILE *f;
    bool obstruct;
    TileType *lreturn;
{
    char *token;
    TileType curlayer = -1;
    lefLayer *lefl = NULL;

    token = LefNextToken(f, TRUE);
    if (*token == ';')
    {
      LefError("Bad Layer statement\n");
      return -1;
    }
    else
    {
      HashEntry *he = HashLookOnly(&LefInfo, token);
      if (he)
      {
          lefl = (lefLayer *)HashGetValue(he);
          if (lefl && obstruct)
          {
            /* Use the obstruction type, if it is defined */
            curlayer = lefl->obsType;
            if ((curlayer < 0) && (lefl->lefClass != CLASS_IGNORE))
                curlayer = lefl->type;
            else if (lefl->lefClass == CLASS_VIA)
                if (lreturn) *lreturn = lefl->info.via.obsType;
          }
          else if (lefl)
          {
            if (lefl->lefClass != CLASS_IGNORE)
                curlayer = lefl->type;
          }
      }
      else
      {
          /* fallback: compare name to magic types */

          curlayer = DBTechNameType(token);
          if (curlayer < 0)
          {
            (void) LefLower(token);
            curlayer = DBTechNameType(token);
          }
      }
      if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE)))
      {
          LefError("Don't know how to parse layer \"%s\"\n", token);
          LefError("Try adding this name to the LEF techfile section\n");
      }
    }
    return curlayer;
}

/*
 *------------------------------------------------------------
 * LefReadLayer --
 *
 *    Read a LEF "LAYER" record from the file, and
 *    match the LEF layer to a magic tile type.
 *    If "obstruct" is TRUE, returns the layer mapping
 *    for obstruction geometry as defined in the
 *    technology file (if it exists).
 *
 * Results:
 *    Returns a magic TileType or -1 if no matching
 *    tile type is found.
 *
 * Side Effects:
 *    Reads input from file f;
 *
 *------------------------------------------------------------
 */

TileType
LefReadLayer(f, obstruct)
    FILE *f;
    bool obstruct;
{
    return LefReadLayers(f, obstruct, (TileType *)NULL);
}

/*
 *------------------------------------------------------------
 * LefReadRect --
 *
 *    Read a LEF "RECT" record from the file, and
 *    return a Rect in magic coordinates.
 *
 * Results:
 *    Returns a pointer to a Rect containing the magic
 *    coordinates, or NULL if an error occurred.
 *
 * Side Effects:
 *    Reads input from file f;
 *
 * Note:
 *    LEF/DEF does NOT define a RECT record as having (...)
 *    pairs, only routes.  However, at least one DEF file
 *    contains this syntax, so it is checked.
 *
 *------------------------------------------------------------
 */

Rect *
LefReadRect(f, curlayer, oscale)
    FILE *f;
    TileType curlayer;
    float oscale;
{
    char *token;
    float llx, lly, urx, ury;
    static Rect paintrect;
    bool needMatch = FALSE;

    token = LefNextToken(f, TRUE);
    if (*token == '(')
    {
      token = LefNextToken(f, TRUE);
      needMatch = TRUE;
    }
    if (!token || sscanf(token, "%f", &llx) != 1) goto parse_error;
    token = LefNextToken(f, TRUE);
    if (!token || sscanf(token, "%f", &lly) != 1) goto parse_error;
    token = LefNextToken(f, TRUE);
    if (needMatch)
    {
      if (*token != ')') goto parse_error;
      else token = LefNextToken(f, TRUE);
      needMatch = FALSE;
    }
    if (*token == '(')
    {
      token = LefNextToken(f, TRUE);
      needMatch = TRUE;
    }
    if (!token || sscanf(token, "%f", &urx) != 1) goto parse_error;
    token = LefNextToken(f, TRUE);
    if (!token || sscanf(token, "%f", &ury) != 1) goto parse_error;
    if (needMatch)
    {
      token = LefNextToken(f, TRUE);
      if (*token != ')') goto parse_error;
    }
    if (curlayer < 0)
      LefError("No layer defined for RECT.\n");
    else
    {
      /* Scale coordinates (microns to magic internal units)      */
      /* Need to scale grid if necessary!             */
            
      paintrect.r_xbot = (int)roundf(llx / oscale);
      paintrect.r_ybot = (int)roundf(lly / oscale);
      paintrect.r_xtop = (int)roundf(urx / oscale);
      paintrect.r_ytop = (int)roundf(ury / oscale);

      /* Diagnostic */
      /*
      TxPrintf("   Painting %s at (%d, %d), (%d, %d) in cell %s.\n",
            DBTypeLongNameTbl[curlayer],
            paintrect.r_xbot, paintrect.r_ybot,
            paintrect.r_xtop, paintrect.r_ytop,
            lefMacro->cd_name);
      */
    }
    return (&paintrect);

parse_error:
    LefError("Bad port geometry: RECT requires 4 values.\n");
    return (Rect *)NULL;
}

/*
 *------------------------------------------------------------
 * LefReadGeometry --
 *
 *    Read Geometry information from a LEF file.
 *    Used for PORT records and OBS statements.
 *
 * Results:
 *    Returns a linked list of all areas and types
 *    painted.  However, if "do_list" is FALSE, then
 *    LefReadGeometry always returns NULL.
 *
 *    Note that do_list is always TRUE for PORT
 *    records and FALSE for OBS statements, and so in
 *    addition to determining what the return value is,
 *    it also determines what layer is returned by
 *    function LefReadLayer().
 *
 * Side Effects:
 *    Reads input from file f;
 *    Paints into the CellDef lefMacro.
 *
 *------------------------------------------------------------
 */

enum lef_geometry_keys {LEF_LAYER = 0, LEF_WIDTH, LEF_PATH,
      LEF_RECT, LEF_POLYGON, LEF_VIA, LEF_GEOMETRY_END};

linkedRect *
LefReadGeometry(lefMacro, f, oscale, do_list)
    CellDef *lefMacro;
    FILE *f;
    float oscale;
    bool do_list;
{
    TileType curlayer = -1, otherlayer = -1;

    char *token;
    int keyword;
    linkedRect *newRect, *rectList;
    Rect *paintrect;

    static char *geometry_keys[] = {
      "LAYER",
      "WIDTH",
      "PATH",
      "RECT",
      "POLYGON",
      "VIA",
      "END",
      NULL
    };

    rectList = NULL;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
      keyword = Lookup(token, geometry_keys);
      if (keyword < 0)
      {
          LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
          LefEndStatement(f);
          continue;
      }
      switch (keyword)
      {
          case LEF_LAYER:
            curlayer = LefReadLayers(f, (do_list) ? FALSE : TRUE, &otherlayer);
            LefEndStatement(f);
            break;
          case LEF_WIDTH:
            LefEndStatement(f);
            break;
          case LEF_PATH:
            LefEndStatement(f);
            break;
          case LEF_RECT:
            paintrect = (curlayer < 0) ? NULL : LefReadRect(f, curlayer, oscale);
            if (paintrect)
            {
                /* Paint the area, if a CellDef is defined */
                if (lefMacro)
                {
                  DBPaint(lefMacro, paintrect, curlayer);
                  if ((!do_list) && (otherlayer != -1))
                        DBPaint(lefMacro, paintrect, otherlayer);
                }

                /* Remember the area and layer */
                if (do_list)
                {
                  newRect = (linkedRect *)mallocMagic(sizeof(linkedRect));
                  newRect->type = curlayer;
                  newRect->area = *paintrect;
                  newRect->rect_next = rectList;
                  rectList = newRect;
                }
            }
            LefEndStatement(f);
            break;
          case LEF_POLYGON:
            LefEndStatement(f);
            break;
          case LEF_VIA:
            LefEndStatement(f);
            break;
          case LEF_GEOMETRY_END:
            if (!LefParseEndStatement(f, NULL))
            {
                LefError("Geometry (PORT or OBS) END statement missing.\n");
                keyword = -1;
            }
            break;
      }
      if (keyword == LEF_GEOMETRY_END) break;
    }
    return rectList;
}

/*
 *------------------------------------------------------------
 * LefReadPort --
 *
 *    A wrapper for LefReadGeometry, which adds a label
 *    to the last rectangle generated by the geometry
 *    parsing.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Reads input from file f;
 *    Paints into the CellDef lefMacro.
 *
 *------------------------------------------------------------
 */

void
LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, oscale)
    CellDef *lefMacro;
    FILE *f;
    char *pinName;
    int pinNum, pinDir, pinUse;
    float oscale;
{
    Label *newlab;
    linkedRect *rectList;

    rectList = LefReadGeometry(lefMacro, f, oscale, TRUE);

    while (rectList != NULL)
    {
      if (pinNum >= 0)
      {
          /* Label this area */
          DBPutLabel(lefMacro, &rectList->area, -1, pinName, rectList->type, 0);

          /* Set this label to be a port */

          if (lefMacro->cd_labels == NULL)
            LefError("Internal error: No labels in cell!\n");
          else
          {
            newlab = lefMacro->cd_lastLabel;
            if (strcmp(newlab->lab_text, pinName))
                LefError("Internal error:  Can't find the label!\n");
            else /* Make this a port */
                newlab->lab_flags = pinNum | pinUse | pinDir | PORT_DIR_MASK;
          }
          /* DBAdjustLabels(lefMacro, &rectList->area); */
      }

      freeMagic((char *)rectList);
      rectList = rectList->rect_next;
    }
}

/*
 *------------------------------------------------------------
 * LefReadPin --
 *
 *    Read a PIN statement from a LEF file.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Reads input from file f;
 *    Paints into the CellDef lefMacro.
 *
 *------------------------------------------------------------
 */

enum lef_pin_keys {LEF_DIRECTION = 0, LEF_USE, LEF_PORT, LEF_CAPACITANCE,
      LEF_PIN_END};

void
LefReadPin(lefMacro, f, pinname, pinNum, oscale)
   CellDef *lefMacro;
   FILE *f;
   char *pinname;
   int pinNum;
   float oscale;
{
    char *token;
    int keyword, subkey;
    int pinDir = PORT_CLASS_DEFAULT;
    int pinUse = PORT_USE_DEFAULT;

    static char *pin_keys[] = {
      "DIRECTION",
      "USE",
      "PORT",
      "CAPACITANCE",
      "END",
      NULL
    };

    static char *pin_classes[] = {
      "DEFAULT",
      "INPUT",
      "OUTPUT TRISTATE",
      "OUTPUT",
      "INOUT",
      "FEEDTHRU",
      NULL
    };

    static int lef_class_to_bitmask[] = {
      PORT_CLASS_DEFAULT,
      PORT_CLASS_INPUT,
      PORT_CLASS_TRISTATE,
      PORT_CLASS_OUTPUT,
      PORT_CLASS_BIDIRECTIONAL,
      PORT_CLASS_FEEDTHROUGH
    };

    static char *pin_uses[] = {
      "DEFAULT",
      "SIGNAL",
      "ANALOG",
      "POWER",
      "GROUND",
      "CLOCK",
      NULL
    };

    static int lef_use_to_bitmask[] = {
      PORT_USE_DEFAULT,
      PORT_USE_SIGNAL,
      PORT_USE_ANALOG,
      PORT_USE_POWER,
      PORT_USE_GROUND,
      PORT_USE_CLOCK
    };

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
      keyword = Lookup(token, pin_keys);
      if (keyword < 0)
      {
          LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
          LefEndStatement(f);
          continue;
      }
      switch (keyword)
      {
          case LEF_DIRECTION:
            token = LefNextToken(f, TRUE);
            subkey = Lookup(token, pin_classes);
            if (subkey < 0)
                LefError("Improper DIRECTION statement\n");
            else
                pinDir = lef_class_to_bitmask[subkey];
            LefEndStatement(f);
            break;
          case LEF_USE:
            token = LefNextToken(f, TRUE);
            subkey = Lookup(token, pin_uses);
            if (subkey < 0)
                LefError("Improper USE statement\n");
            else
                pinUse = lef_use_to_bitmask[subkey];
            LefEndStatement(f);
            break;
          case LEF_PORT:
            LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, oscale);
            break;
          case LEF_CAPACITANCE:
            LefEndStatement(f);     /* Ignore. . . */
            break;
          case LEF_PIN_END:
            if (!LefParseEndStatement(f, pinname))
            {
                LefError("Pin END statement missing.\n");
                keyword = -1;
            }
            break;
      }
      if (keyword == LEF_PIN_END) break;
    }
}

/*
 *------------------------------------------------------------
 * LefEndStatement --
 *
 *    Read file input to EOF or a ';' token (end-of-statement)
 *
 *------------------------------------------------------------
 */

void
LefEndStatement(f)
    FILE *f;
{
    char *token;

    while ((token = LefNextToken(f, TRUE)) != NULL)
      if (*token == ';') break;
}

/*
 *------------------------------------------------------------
 *
 * LefReadMacro --
 *
 *    Read in a MACRO section from a LEF file.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Creates a new cell definition in the magic database.
 *
 *------------------------------------------------------------
 */

enum lef_macro_keys {LEF_CLASS = 0, LEF_SIZE, LEF_ORIGIN,
      LEF_SYMMETRY, LEF_SOURCE, LEF_SITE, LEF_PIN, LEF_OBS,
      LEF_TIMING, LEF_FOREIGN, LEF_MACRO_END};

void
LefReadMacro(f, mname, oscale, importForeign)
    FILE *f;                  /* LEF file being read  */
    char *mname;        /* name of the macro    */
    float oscale;       /* scale factor um->magic units */
    bool importForeign;       /* Whether we should try to read 
                         * in a cell.
                         */
{
    CellDef *lefMacro;
    HashEntry *he;

    char *token, tsave[128];
    int keyword, pinNum;
    float x, y;
    bool has_size, is_imported = FALSE;
    Rect lefBBox;

    static char *macro_keys[] = {
      "CLASS",
      "SIZE",
      "ORIGIN",
      "SYMMETRY",
      "SOURCE",
      "SITE",
      "PIN",
      "OBS",
      "TIMING",
      "FOREIGN",
      "END",
      NULL
    };

    /* Start by creating a new celldef */

    he = HashFind(&lefDefInitHash, mname);
    if (HashGetValue(he))
    {
      int suffix;
      char newname[256];

      for (suffix = 1; HashGetValue(he) != NULL; suffix++)
      {
          sprintf(newname, "%250s_%d", mname, suffix);
          he = HashFind(&lefDefInitHash, newname);
      }
      LefError("Cell \"%s\" was already defined in this file.  "
            "Renaming this cell \"%s\"\n", mname, newname);
      lefMacro = lefFindCell(newname);
    }
    else
      lefMacro = lefFindCell(mname);

    DBCellClearDef(lefMacro);
    DBCellSetAvail(lefMacro);
    HashSetValue(he, lefMacro);

    /* Initial values */
    pinNum = 1;
    has_size = FALSE;
    lefBBox.r_xbot = 0;
    lefBBox.r_ybot = 0;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
      keyword = Lookup(token, macro_keys);
      if (keyword < 0)
      {
          LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
          LefEndStatement(f);
          continue;
      }
      switch (keyword)
      {
          case LEF_CLASS:
            token = LefNextToken(f, TRUE);
            if (*token != '\n')
                DBPropPut(lefMacro, "LEFclass", token);
            LefEndStatement(f);
            break;
          case LEF_SIZE:
            token = LefNextToken(f, TRUE);
            if (!token || sscanf(token, "%f", &x) != 1) goto size_error;
            token = LefNextToken(f, TRUE);            /* skip keyword "BY" */
            if (!token) goto size_error;
            token = LefNextToken(f, TRUE);
            if (!token || sscanf(token, "%f", &y) != 1) goto size_error;

            lefBBox.r_xtop = (int)roundf(x / oscale) + lefBBox.r_xbot;
            lefBBox.r_ytop = (int)roundf(y / oscale) + lefBBox.r_ybot;
            has_size = TRUE;
            LefEndStatement(f);
            break;
size_error:
            LefError("Bad macro SIZE; requires values X BY Y.\n");
            LefEndStatement(f);
            break;
          case LEF_ORIGIN:
            token = LefNextToken(f, TRUE);
            if (!token || sscanf(token, "%f", &x) != 1) goto origin_error;
            token = LefNextToken(f, TRUE);
            if (!token || sscanf(token, "%f", &y) != 1) goto origin_error;

            lefBBox.r_xbot = -(int)roundf(x / oscale);
            lefBBox.r_ybot = -(int)roundf(y / oscale);
            if (has_size)
            {
                lefBBox.r_xtop += lefBBox.r_xbot;
                lefBBox.r_ytop += lefBBox.r_ybot;
            }
            LefEndStatement(f);
            break;
origin_error:
            LefError("Bad macro ORIGIN; requires 2 values.\n");
            LefEndStatement(f);
            break;
          case LEF_SYMMETRY:
            token = LefNextToken(f, TRUE);
            if (*token != '\n')
                DBPropPut(lefMacro, "LEFsymmetry", token + strlen(token) + 1);
            LefEndStatement(f);
            break;
          case LEF_SOURCE:
            token = LefNextToken(f, TRUE);
            if (*token != '\n')
                DBPropPut(lefMacro, "LEFsource", token);
            LefEndStatement(f);
            break;
          case LEF_SITE:
            token = LefNextToken(f, TRUE);
            if (*token != '\n')
                DBPropPut(lefMacro, "LEFsite", token);
            LefEndStatement(f);
            break;
          case LEF_PIN:
            token = LefNextToken(f, TRUE);
            /* Diagnostic */
            /*
            TxPrintf("   Macro defines pin %s\n", token);
            */
            sprintf(tsave, "%.127s", token);
            if (is_imported)
                LefSkipSection(f, tsave);
            else
                LefReadPin(lefMacro, f, tsave, pinNum++, oscale);
            break;
          case LEF_OBS:
            /* Diagnostic */
            /*
            TxPrintf("   Macro defines obstruction\n");
            */
            if (is_imported)
                LefSkipSection(f, NULL);
            else
                LefReadGeometry(lefMacro, f, oscale, FALSE);
            break;
          case LEF_TIMING:
            LefSkipSection(f, macro_keys[LEF_TIMING]);
            break;
          case LEF_FOREIGN:
            if (importForeign)
            {
                token = LefNextToken(f, TRUE);
                sprintf(tsave, "%.127s", token);

                /* Attempt to read a .mag file of this name */

                DBCellClearAvail(lefMacro);
                if (DBCellRead(lefMacro, tsave, TRUE))
                  is_imported = TRUE;
                else
                {
                  TxError("   Cannot find magic file %s.mag "
                        "to import.\n", tsave);
                  TxError("   Filling cell with geometry from "
                        "the LEF file.\n");
                }
            }
            LefEndStatement(f);
            break;
          case LEF_MACRO_END:
            if (!LefParseEndStatement(f, mname))
            {
                LefError("Macro END statement missing.\n");
                keyword = -1;
            }
            break;
      }
      if (keyword == LEF_MACRO_END) break;
    }

    /* Finish up creating the cell */

    if (is_imported)
      DBReComputeBbox(lefMacro);
    else
    {
      DBAdjustLabelsNew(lefMacro, &TiPlaneRect, 1);

      if (!has_size)
      {
          LefError("   Macro does not define size:  computing from geometry\n");
          DBReComputeBbox(lefMacro);
      }
      else
          lefMacro->cd_bbox = lefBBox;

      /* Fix the bounding box and do not allow edits */
      lefMacro->cd_flags |= /* CDNOEDIT | */ CDFIXEDBBOX;

      DRCCheckThis(lefMacro, TT_CHECKPAINT, &lefMacro->cd_bbox);
    }
    DBWAreaChanged(lefMacro, &lefMacro->cd_bbox, DBW_ALLWINDOWS,
            &DBAllButSpaceBits);
}

/*
 *------------------------------------------------------------
 *
 * LefAddViaGeometry --
 *
 *    Read in geometry for a VIA section from a LEF or DEF
 *    file.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Adds to the lefLayer record for a via definition.
 *
 *------------------------------------------------------------
 */

void
LefAddViaGeometry(f, lefl, curlayer, oscale)
    FILE *f;                  /* LEF file being read  */
    lefLayer *lefl;           /* pointer to via info  */
    TileType curlayer;        /* current tile type    */
    float oscale;       /* output scaling */
{
    Rect *currect;
    linkedRect *viaLR;

    /* Rectangles for vias are read in units of 1/2 lambda */
    currect = LefReadRect(f, curlayer, (oscale / 2));
    if (currect == NULL) return;

    /* For LEF contact types matching magic contact types,  */
    /* size the LEF contact cut to cover the minimum        */
    /* rectangle in the other layers that satisfies the           */
    /* CIF/GDS contact generation.                    */

    if (DBIsContact(curlayer))
    {
      int edgeSize = 0, contSize, halfSize;
      float fcontSize;

      /* Get the minimum size of a contact (cut + borders) from cifoutput */
      contSize = CIFGetContactSize(curlayer, &edgeSize, NULL, NULL);

      /* All internal LEF via geometry values are doubled */
      contSize <<= 1;   
      edgeSize <<= 1;   

        if (contSize % CIFCurStyle->cs_scaleFactor == 0)
         contSize /= CIFCurStyle->cs_scaleFactor;
      else
         contSize = contSize / CIFCurStyle->cs_scaleFactor + 1;

        if (edgeSize % CIFCurStyle->cs_scaleFactor == 0)
         edgeSize /= CIFCurStyle->cs_scaleFactor;
      else
         edgeSize = edgeSize / CIFCurStyle->cs_scaleFactor + 1;

      if (edgeSize > 0 && contSize > 0)
      {
          /* Flag a warning if the cut size is different from what's expected */
          if ((currect->r_xtop - currect->r_xbot != edgeSize) ||
                  (currect->r_ytop - currect->r_ybot != edgeSize))
          {
            LefError("Warning: Cut size for magic type \"%s\" (%d x %d) does "
                  "not match LEF/DEF\n",
                  DBTypeLongNameTbl[lefl->type],
                  edgeSize, edgeSize);
            LefError("  via cut size (%d x %d).  Magic layer cut size will "
                  "be used!\n",
                  currect->r_xtop - currect->r_xbot,
                  currect->r_ytop - currect->r_ybot);
          }

          halfSize = contSize >> 1;
          currect->r_xbot = ((currect->r_xbot + currect->r_xtop) / 2) - halfSize;
          currect->r_ybot = ((currect->r_ybot + currect->r_ytop) / 2) - halfSize;
          currect->r_xtop = currect->r_xbot + contSize;
          currect->r_ytop = currect->r_ybot + contSize;
      }
    }

    if (GEO_SAMERECT(lefl->info.via.area, GeoNullRect))
    {
      lefl->info.via.area = *currect;
      lefl->type = curlayer;
    }
    else 
    {
      viaLR = (linkedRect *)mallocMagic(sizeof(linkedRect));
      viaLR->rect_next = lefl->info.via.lr;
      lefl->info.via.lr = viaLR;
      viaLR->type = curlayer;
      viaLR->area = *currect;

      /* Make sure that the primary record is a contact type. */
      if (DBIsContact(curlayer) && !DBIsContact(lefl->type))
      {
          viaLR->type = lefl->type;
          lefl->type = curlayer;
          viaLR->area = lefl->info.via.area;
          lefl->info.via.area = *currect;
      }
    }
}

/*
 *------------------------------------------------------------
 *
 * LefReadLayerSection --
 *
 *    Read in a LAYER, VIA, or VIARULE section from a LEF file.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Adds to the LEF layer info hash table.
 *
 *------------------------------------------------------------
 */

enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH, LEF_LAYER_SPACING,
      LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET,
      LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT,
      LEF_VIARULE_VIA, LEF_LAYER_END};

void
LefReadLayerSection(f, lname, mode, lefl)
    FILE *f;                  /* LEF file being read    */
    char *lname;        /* name of the layer      */
    int mode;                 /* layer, via, or viarule */
    lefLayer *lefl;           /* pointer to layer info  */
{
    char *token;
    int keyword, typekey;
    Rect viaArea;
    TileType curlayer = -1;
    float fvalue, oscale;

    /* These are defined in the order of CLASS_* in lefInt.h */
    static char *layer_type_keys[] = {
      "ROUTING",
      "CUT",
      "MASTERSLICE",
      "OVERLAP",
      NULL
    };

    static char *layer_keys[] = {
      "TYPE",
      "WIDTH",
      "SPACING",
      "PITCH",
      "DIRECTION",
      "OFFSET",
      "DEFAULT",
      "LAYER",
      "RECT",
      "VIA",
      "END",
      NULL
    };

    oscale = CIFGetOutputScale(1000);
    viaArea = GeoNullRect;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
      keyword = Lookup(token, layer_keys);
      if (keyword < 0)
      {
          LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
          LefEndStatement(f);
          continue;
      }
      switch (keyword)
      {
          case LEF_LAYER_TYPE:
            token = LefNextToken(f, TRUE);
            if (*token != '\n')
            {
                typekey = Lookup(token, layer_type_keys);
                if (typekey < 0)
                  LefError("Unknown layer type \"%s\" in LEF file; "
                        "ignoring.\n", token);
            }
            if (lefl->lefClass != typekey)
                LefError("Attempt to reclassify layer %s from %s to %s\n",
                        lname, layer_type_keys[lefl->lefClass],
                        layer_type_keys[typekey]);
            LefEndStatement(f);
            break;
          case LEF_LAYER_WIDTH:
            token = LefNextToken(f, TRUE);
            sscanf(token, "%f", &fvalue);
            lefl->info.route.width = (int)roundf(fvalue / oscale);
            LefEndStatement(f);
            break;
          case LEF_LAYER_SPACING:
            token = LefNextToken(f, TRUE);
            sscanf(token, "%f", &fvalue);
            lefl->info.route.spacing = (int)roundf(fvalue / oscale);
            LefEndStatement(f);
            break;
          case LEF_LAYER_PITCH:
            token = LefNextToken(f, TRUE);
            sscanf(token, "%f", &fvalue);
            lefl->info.route.pitch = (int)roundf(fvalue / oscale);
            LefEndStatement(f);
            break;
          case LEF_LAYER_DIRECTION:
            token = LefNextToken(f, TRUE);
            LefLower(token);
            lefl->info.route.hdirection = (token[0] == 'h') ? TRUE : FALSE;
            LefEndStatement(f);
            break;
          case LEF_LAYER_OFFSET:
            LefEndStatement(f);
            break;
          case LEF_VIA_DEFAULT:
            /* Do nothing; especially, don't look for end-of-statement! */
            break;
          case LEF_VIA_LAYER:
            curlayer = LefReadLayer(f, FALSE);
            LefEndStatement(f);
            break;
          case LEF_VIA_RECT:
            LefAddViaGeometry(f, lefl, curlayer, oscale);
            LefEndStatement(f);
            break;
          case LEF_VIARULE_VIA:
            LefEndStatement(f);
            break;
          case LEF_LAYER_END:
            if (!LefParseEndStatement(f, lname))
            {
                LefError("Layer END statement missing.\n");
                keyword = -1;
            }
            break;
      }
      if (keyword == LEF_LAYER_END) break;
    }
}

/*
 *------------------------------------------------------------
 *
 * LefRead --
 *
 *    Read a .lef file into a magic layout.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Many.  Cell definitions and uses are created and added to
 *    the database.
 *
 *------------------------------------------------------------
 */

enum lef_sections {LEF_VERSION = 0, LEF_NAMESCASESENSITIVE,
      LEF_PROPERTYDEFS, LEF_UNITS, LEF_SECTION_LAYER,
      LEF_SECTION_VIA, LEF_SECTION_VIARULE,
      LEF_SECTION_SPACING, LEF_SECTION_SITE, LEF_PROPERTY,
      LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP,
      LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO,
      LEF_END};

void
LefRead(inName, importForeign)
    char *inName;
    bool importForeign;
{
    FILE *f;
    char *filename;
    char *token;
    char tsave[128];
    int keyword;
    float oscale;
    HashEntry *he;
    lefLayer *lefl;

    static char *sections[] = {
      "VERSION",
      "NAMESCASESENSITIVE",
      "PROPERTYDEFINITIONS",
      "UNITS",
      "LAYER",
      "VIA",
      "VIARULE",
      "SPACING",
      "SITE",
      "PROPERTY",
      "NOISETABLE",
      "CORRECTIONTABLE",
      "IRDROP",
      "ARRAY",
      "TIMING",
      "BEGINEXT",
      "MACRO",
      "END",
      NULL
    };

    /* 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, ".lef", "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
      return;
    }

    TxPrintf("Reading LEF data from file %s.\n", filename);
    TxPrintf("This action is undoable.\n");
    UndoDisable();

    /* Initialize */
    HashInit(&LefCellTable, 32, HT_STRINGKEYS);
    HashInit(&lefDefInitHash, 32, HT_STRINGKEYS);
    oscale = CIFGetOutputScale(1000);

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
      keyword = Lookup(token, sections);
      if (keyword < 0)
      {
          LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
          LefEndStatement(f);
          continue;
      }
      switch (keyword)
      {
          case LEF_VERSION:
            LefEndStatement(f);
            break;
          case LEF_NAMESCASESENSITIVE:
            LefEndStatement(f);
            break;
          case LEF_PROPERTYDEFS:
            LefSkipSection(f, sections[LEF_PROPERTYDEFS]);
            break;
          case LEF_UNITS:
            LefSkipSection(f, sections[LEF_UNITS]);
            break;

          case LEF_SECTION_VIA:
          case LEF_SECTION_VIARULE:
            token = LefNextToken(f, TRUE);
            sprintf(tsave, "%.127s", token);
            he = HashFind(&LefInfo, token);
            lefl = (lefLayer *)HashGetValue(he);
            if (lefl == NULL)
            {
                lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
                lefl->type = -1;
                lefl->obsType = -1;
                lefl->refCnt = 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);
                LefReadLayerSection(f, tsave, keyword, lefl);
                lefl->canonName = (char *)he->h_key.h_name;
            }
            else if (keyword == LEF_SECTION_VIARULE)
                /* If we've already seen this via, don't reprocess. */
                /* This deals with VIA followed by VIARULE.  We   */
                /* really ought to have special processing for the      */
                /* VIARULE section. . .                     */
                LefSkipSection(f, tsave);
            else
            {
                LefError("Warning:  Cut type \"%s\" redefined.\n", token);
                lefl = LefRedefined(lefl, token);
                LefReadLayerSection(f, tsave, keyword, lefl);
            }
            break;

          case LEF_SECTION_LAYER:
            token = LefNextToken(f, TRUE);
            sprintf(tsave, "%.127s", token);
            he = HashLookOnly(&LefInfo, token);
            if (he == NULL)
            {
                TileType mtype = DBTechNameType(token);
                if (mtype < 0)
                  mtype = DBTechNameType(LefLower(token));
                if (mtype < 0)
                {
                  LefError("Layer %s cannot be mapped to any magic layer!\n",
                        token);
                  LefSkipSection(f, tsave);
                  break;
                }
                else if (DBIsContact(mtype) && (keyword == LEF_SECTION_LAYER))
                {
                  LefError("Layer %s maps to a magic contact layer; "
                        "must be defined in lef section of techfile\n",
                        token);
                  LefSkipSection(f, tsave);
                  break;
                }
                else if (!DBIsContact(mtype) && (keyword != LEF_SECTION_LAYER))
                {
                  LefError("Via %s maps to a non-contact magic layer; "
                        "must be defined in lef section of techfile\n",
                        token);
                  LefSkipSection(f, tsave);
                  break;
                }
                else
                {
                  he = HashFind(&LefInfo, token);
                  lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
                  lefl->type = mtype;
                  lefl->obsType = -1;
                  lefl->refCnt = 1;
                  lefl->lefClass = (DBIsContact(mtype)) ? CLASS_VIA : CLASS_ROUTE;
                  HashSetValue(he, lefl);
                    lefl->canonName = (char *)he->h_key.h_name;
                }
            }
            else
            {
                lefl = (lefLayer *)HashGetValue(he);
                if (lefl && lefl->type < 0)
                {
                  LefError("Layer %s is only defined for obstructions!\n", token);
                  LefSkipSection(f, tsave);
                  break;
                }
            }
            LefReadLayerSection(f, tsave, keyword, lefl);
            break;

          case LEF_SECTION_SPACING:
            LefSkipSection(f, sections[LEF_SECTION_SPACING]);
            break;
          case LEF_SECTION_SITE:
            token = LefNextToken(f, TRUE);
            TxPrintf("LEF file:  Defines site %s (ignored)\n", token);
            sprintf(tsave, "%.127s", token);
            LefSkipSection(f, tsave);
            break;
          case LEF_PROPERTY:
            LefSkipSection(f, NULL);
            break;
          case LEF_NOISETABLE:
            LefSkipSection(f, sections[LEF_NOISETABLE]);
            break;
          case LEF_CORRECTIONTABLE:
            LefSkipSection(f, sections[LEF_CORRECTIONTABLE]);
            break;
          case LEF_IRDROP:
            LefSkipSection(f, sections[LEF_IRDROP]);
            break;
          case LEF_ARRAY:
            LefSkipSection(f, sections[LEF_ARRAY]);
            break;
          case LEF_SECTION_TIMING:
            LefSkipSection(f, sections[LEF_SECTION_TIMING]);
            break;
          case LEF_EXTENSION:
            LefSkipSection(f, sections[LEF_EXTENSION]);
            break;
          case LEF_MACRO:
            token = LefNextToken(f, TRUE);
            /* Diagnostic */
            /*
            TxPrintf("LEF file:  Defines new cell %s\n", token);
            */
            sprintf(tsave, "%.127s", token);
            LefReadMacro(f, tsave, oscale, importForeign);
            break;
          case LEF_END:
            if (!LefParseEndStatement(f, "LIBRARY"))
            {
                LefError("END statement out of context.\n");
                keyword = -1;
            }
            break;
      }
      if (keyword == LEF_END) break;
    }
    TxPrintf("LEF read: Processed %d lines.\n", lefCurrentLine);
    LefError(NULL);     /* print statement of errors, if any */

    /* Cleanup */
    HashKill(&LefCellTable);
    HashKill(&lefDefInitHash);
    if (f != NULL) fclose(f);
    UndoEnable();
}

Generated by  Doxygen 1.6.0   Back to index