2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ExtTech.c --
|
|
|
|
|
*
|
|
|
|
|
* Circuit extraction.
|
|
|
|
|
* Code to read and process the sections of a technology file
|
|
|
|
|
* that are specific to circuit extraction.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
|
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
|
|
|
* * software and its documentation for any purpose and without *
|
|
|
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
|
|
|
* * notice appear in all copies. The University of California *
|
|
|
|
|
* * makes no representations about the suitability of this *
|
|
|
|
|
* * software for any purpose. It is provided "as is" without *
|
|
|
|
|
* * express or implied warranty. Export of this software outside *
|
|
|
|
|
* * of the United States of America may require an export license. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char sccsid[] = "@(#)ExtTech.c 4.8 MAGIC (Berkeley) 10/26/85";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h> /* for strtod() */
|
|
|
|
|
#include <string.h>
|
2024-10-04 19:59:39 +02:00
|
|
|
#include <strings.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
#include <math.h>
|
|
|
|
|
#include <ctype.h> /* for isspace() */
|
|
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "database/databaseInt.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "utils/tech.h"
|
|
|
|
|
#include "debug/debug.h"
|
|
|
|
|
#include "extract/extract.h"
|
|
|
|
|
#include "extract/extractInt.h"
|
|
|
|
|
#include "cif/CIFint.h"
|
2019-01-29 22:41:48 +01:00
|
|
|
#include "cif/cif.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Whether we are converting units from microns to lambda */
|
|
|
|
|
bool doConvert;
|
|
|
|
|
|
|
|
|
|
/* Current extraction style */
|
|
|
|
|
ExtStyle *ExtCurStyle = NULL;
|
|
|
|
|
|
|
|
|
|
/* List of all styles */
|
|
|
|
|
ExtKeep *ExtAllStyles = NULL;
|
|
|
|
|
|
2022-01-14 23:30:05 +01:00
|
|
|
/* Mask of all types found in the extract section */
|
2023-02-22 23:56:45 +01:00
|
|
|
TileTypeBitMask *allExtractTypes = NULL;
|
2022-01-14 23:30:05 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Forward declarations */
|
|
|
|
|
void extTechFinalStyle();
|
|
|
|
|
void ExtLoadStyle();
|
|
|
|
|
void ExtTechScale(int, int);
|
|
|
|
|
|
2023-01-27 17:47:37 +01:00
|
|
|
/* This is a placeholder value; it may be approximately
|
|
|
|
|
* constant across processes, or it may need to be set
|
|
|
|
|
* per process.
|
|
|
|
|
*/
|
|
|
|
|
#define FRINGE_MULT 0.02
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* Table used for parsing the extract section of a .tech file
|
|
|
|
|
* Each line in the extract section is of a type determined by
|
|
|
|
|
* its first keyword. There is one entry in the following table
|
|
|
|
|
* for each such keyword.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
AREAC, CONTACT, CSCALE,
|
|
|
|
|
DEFAULTAREACAP, DEFAULTOVERLAP, DEFAULTPERIMETER, DEFAULTSIDEOVERLAP,
|
|
|
|
|
DEFAULTSIDEWALL,
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
DEVICE, DEVRESIST, FET, FETRESIST, FRINGESHIELDHALO,
|
2022-03-17 22:35:41 +01:00
|
|
|
HEIGHT, ANTENNA, MODEL, TIEDOWN, LAMBDA, OVERC,
|
2017-04-25 14:41:48 +02:00
|
|
|
PERIMC, PLANEORDER, NOPLANEORDER, RESIST, RSCALE, SIDEHALO, SIDEOVERLAP,
|
|
|
|
|
SIDEWALL, STEP, STYLE, SUBSTRATE, UNITS, VARIANT
|
|
|
|
|
} Key;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
2024-10-10 21:13:24 +02:00
|
|
|
const char *k_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
int k_key;
|
|
|
|
|
int k_minargs;
|
|
|
|
|
int k_maxargs;
|
2024-10-10 21:13:24 +02:00
|
|
|
const char *k_usage;
|
2017-04-25 14:41:48 +02:00
|
|
|
} keydesc;
|
|
|
|
|
|
2024-10-10 21:13:24 +02:00
|
|
|
static const keydesc keyTable[] = {
|
2024-10-04 18:02:09 +02:00
|
|
|
{"areacap", AREAC, 3, 3,
|
|
|
|
|
"types capacitance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"contact", CONTACT, 3, 6,
|
|
|
|
|
"type resistance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"cscale", CSCALE, 2, 2,
|
|
|
|
|
"capacitance-scalefactor"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"defaultareacap", DEFAULTAREACAP, 4, 6,
|
|
|
|
|
"types plane capacitance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"defaultoverlap", DEFAULTOVERLAP, 6, 6,
|
|
|
|
|
"types plane otertypes otherplane capacitance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"defaultperimeter", DEFAULTPERIMETER, 4, 6,
|
|
|
|
|
"types plane capacitance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"defaultsideoverlap", DEFAULTSIDEOVERLAP, 6, 6,
|
|
|
|
|
"types plane othertypes otherplane capacitance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"defaultsidewall", DEFAULTSIDEWALL, 4, 5,
|
|
|
|
|
"types plane capacitance [offset]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"device", DEVICE, 4, 10,
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
"dev-type dev-name types options..."},
|
|
|
|
|
|
|
|
|
|
{"devresist", DEVRESIST, 4, 4,
|
|
|
|
|
"type region ohms-per-square"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"fet", FET, 8, 9,
|
|
|
|
|
"types terminal-types min-#-terminals name [subs-types] subs-node gscap gate-chan-cap"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"fetresist", FETRESIST, 4, 4,
|
|
|
|
|
"type region ohms-per-square"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"fringeshieldhalo", FRINGESHIELDHALO, 2, 2,
|
|
|
|
|
"distance"},
|
2022-03-17 22:35:41 +01:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"height", HEIGHT, 4, 4,
|
|
|
|
|
"type height-above-subtrate thickness"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"antenna", ANTENNA, 4, 6,
|
|
|
|
|
"type [calc-type] [antenna-ratio-proportional] antenna-ratio-const"},
|
2019-10-17 02:53:03 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"model", MODEL, 2, 3,
|
|
|
|
|
"partial-cumulative [area-sidewall]"},
|
2019-10-21 04:12:02 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"tiedown", TIEDOWN, 2, 2,
|
|
|
|
|
"types"},
|
2019-10-18 20:12:52 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"lambda", LAMBDA, 2, 2,
|
|
|
|
|
"units-per-lambda"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"overlap", OVERC, 4, 5,
|
|
|
|
|
"toptypes bottomtypes capacitance [shieldtypes]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"perimc", PERIMC, 4, 4,
|
|
|
|
|
"intypes outtypes capacitance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"planeorder", PLANEORDER, 3, 3,
|
|
|
|
|
"plane index"},
|
|
|
|
|
{"noplaneordering", NOPLANEORDER, 1, 1,
|
|
|
|
|
"(no arguments needed)"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"resist", RESIST, 3, 4,
|
|
|
|
|
"types resistance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"rscale", RSCALE, 2, 2,
|
|
|
|
|
"resistance-scalefactor"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"sidehalo", SIDEHALO, 2, 2,
|
|
|
|
|
"distance"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"sideoverlap", SIDEOVERLAP, 5, 6,
|
|
|
|
|
"intypes outtypes ovtypes capacitance [shieldtypes]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"sidewall", SIDEWALL, 6, 7,
|
|
|
|
|
"intypes outtypes neartypes fartypes capacitance [offset]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"step", STEP, 2, 2,
|
|
|
|
|
"size"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"style", STYLE, 2, 4,
|
|
|
|
|
"stylename"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"substrate", SUBSTRATE, 3, 5,
|
|
|
|
|
"types plane [subs-node]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"units", UNITS, 2, 2,
|
|
|
|
|
"lambda|microns"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"variants", VARIANT, 2, 2,
|
|
|
|
|
"style,..."},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{0}
|
2017-04-25 14:41:48 +02:00
|
|
|
};
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Table used for parsing the "device" keyword types
|
|
|
|
|
*
|
|
|
|
|
* (Note: "10" for max types in subcircuit is arbitrary---the parser
|
2025-08-07 17:54:49 +02:00
|
|
|
* ignores max types for DEV_SUBCKT, DEV_MSUBCKT, and DEV_VERILOGA).
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* types are enumerated in extract.h */
|
|
|
|
|
|
2024-10-10 21:13:24 +02:00
|
|
|
static const keydesc devTable[] = {
|
2024-10-04 18:02:09 +02:00
|
|
|
{"mosfet", DEV_MOSFET, 5, 10,
|
|
|
|
|
"name gate-types src-types [drn-types] sub-types|None sub-node [gscap gccap]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"bjt", DEV_BJT, 5, 5,
|
|
|
|
|
"name base-types emitter-types collector-types"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"capacitor", DEV_CAP, 4, 8,
|
|
|
|
|
"name top-types bottom-types [sub-types|None sub-node] [[perimcap] areacap]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"capreverse", DEV_CAPREV, 4, 8,
|
|
|
|
|
"name bottom-types top-types [sub-types|None sub-node] [[perimcap] areacap]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"resistor", DEV_RES, 4, 6,
|
|
|
|
|
"name|None res-types terminal-types [sub-types|None sub-node]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"diode", DEV_DIODE, 4, 6,
|
|
|
|
|
"name pos-types neg-types [sub-types|None sub-node]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"pdiode", DEV_PDIODE, 4, 6,
|
|
|
|
|
"name pos-types neg-types [sub-types|None sub-node]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"ndiode", DEV_NDIODE, 4, 6,
|
|
|
|
|
"name neg-types pos-types [sub-types|None sub-node]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"subcircuit", DEV_SUBCKT, 3, 11,
|
|
|
|
|
"name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"rsubcircuit", DEV_RSUBCKT, 4, 7,
|
|
|
|
|
"name dev-types terminal-types [sub-types|None sub-node] [options]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"msubcircuit", DEV_MSUBCKT, 3, 11,
|
|
|
|
|
"name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"},
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{"csubcircuit", DEV_CSUBCKT, 4, 7,
|
|
|
|
|
"name dev-types terminal-types [sub-types|None sub-node] [options]"},
|
2018-10-30 21:19:20 +01:00
|
|
|
|
2025-08-07 17:54:49 +02:00
|
|
|
{"veriloga", DEV_VERILOGA, 3, 11,
|
|
|
|
|
"name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"},
|
|
|
|
|
|
2024-10-04 18:02:09 +02:00
|
|
|
{0}
|
2017-04-25 14:41:48 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtCompareStyle --
|
|
|
|
|
*
|
|
|
|
|
* This routine is designed to work with embedded exttosim and
|
|
|
|
|
* exttospice. It determines whether the current extract style
|
|
|
|
|
* matches the string (picked up from the .ext file). If so, it
|
|
|
|
|
* returns TRUE. If not, it checks whether the style exists in
|
|
|
|
|
* the list of known files for this technology. If so, it loads
|
|
|
|
|
* the correct style and returns TRUE. If not, it returns FALSE.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ExtCompareStyle(stylename)
|
|
|
|
|
char *stylename;
|
|
|
|
|
{
|
|
|
|
|
ExtKeep *style;
|
|
|
|
|
|
|
|
|
|
if (!strcmp(stylename, ExtCurStyle->exts_name))
|
|
|
|
|
return TRUE;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (style = ExtAllStyles; style != NULL; style = style->exts_next)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(stylename, style->exts_name))
|
|
|
|
|
{
|
|
|
|
|
ExtLoadStyle(stylename);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 22:12:07 +01:00
|
|
|
#endif /* MAGIC_WRAPPER */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtGetDevInfo --
|
|
|
|
|
*
|
|
|
|
|
* This routine is designed to work with the embedded exttosim and
|
|
|
|
|
* exttospice commands under the Tcl-based magic, such that all
|
|
|
|
|
* device information needed by these commands can be picked up
|
|
|
|
|
* from the current extraction style (as it should!). This
|
|
|
|
|
* really should be set up when the extract file is read, which is
|
|
|
|
|
* why the routine is here, although this is not a very efficient
|
|
|
|
|
* way to do it (but it needs to be this way to keep backward
|
|
|
|
|
* compatibility with the non-Tcl, standalone programs ext2sim and
|
|
|
|
|
* ext2spice).
|
|
|
|
|
*
|
|
|
|
|
* Note that finding the device by index ("idx") is horribly
|
|
|
|
|
* inefficient, but keeps the netlist generator separated from
|
|
|
|
|
* the extractor. Some of this code is seriously schizophrenic,
|
|
|
|
|
* and should not be investigated too closely.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Results:
|
|
|
|
|
* Return FALSE if no device corresponds to index "idx". TRUE
|
|
|
|
|
* otherwise.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Fills values in the argument list.
|
2020-09-11 23:29:12 +02:00
|
|
|
*
|
|
|
|
|
* Notes:
|
|
|
|
|
* The original sd_rclassptr has been expanded to s_rclassptr and
|
|
|
|
|
* d_rclassptr to capture asymmetric devices, bipolars, etc. Note
|
|
|
|
|
* that this is not a general-purpose method extending beyond two
|
|
|
|
|
* (non-gate) terminals, and should be updated.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2020-10-20 18:22:02 +02:00
|
|
|
ExtGetDevInfo(idx, devnameptr, devtypeptr, s_rclassptr, d_rclassptr,
|
|
|
|
|
sub_rclassptr, subnameptr)
|
2017-04-25 14:41:48 +02:00
|
|
|
int idx;
|
2020-10-20 18:22:02 +02:00
|
|
|
char **devnameptr; /* Name of extracted device model */
|
|
|
|
|
TileType *devtypeptr; /* Magic tile type of device */
|
2020-09-11 23:29:12 +02:00
|
|
|
short *s_rclassptr; /* Source (1st terminal) type only */
|
|
|
|
|
short *d_rclassptr; /* Drain (2nd terminal) type only */
|
2017-04-25 14:41:48 +02:00
|
|
|
short *sub_rclassptr;
|
|
|
|
|
char **subnameptr;
|
|
|
|
|
{
|
|
|
|
|
TileType t;
|
|
|
|
|
TileTypeBitMask *rmask, *tmask;
|
|
|
|
|
int n, i = 0, j;
|
2019-10-14 17:56:39 +02:00
|
|
|
bool repeat, found;
|
2019-08-19 20:11:02 +02:00
|
|
|
ExtDevice *devptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *locdname;
|
|
|
|
|
char **uniquenamelist = (char **)mallocMagic(DBNumTypes * sizeof(char *));
|
|
|
|
|
|
2019-10-14 17:56:39 +02:00
|
|
|
found = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
locdname = devptr->exts_deviceName;
|
|
|
|
|
if (locdname != NULL)
|
|
|
|
|
{
|
|
|
|
|
repeat = FALSE;
|
|
|
|
|
for (j = 0; j < i; j++)
|
|
|
|
|
if (!strcmp(uniquenamelist[j], locdname))
|
|
|
|
|
{
|
|
|
|
|
repeat = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (repeat == FALSE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-14 17:56:39 +02:00
|
|
|
if (i == idx)
|
|
|
|
|
{
|
|
|
|
|
found = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-08-19 20:11:02 +02:00
|
|
|
uniquenamelist[i] = locdname;
|
|
|
|
|
i++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-14 17:56:39 +02:00
|
|
|
if (found == TRUE) break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-12-13 22:12:30 +01:00
|
|
|
if (t == DBNumTypes)
|
|
|
|
|
{
|
|
|
|
|
freeMagic(uniquenamelist);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (devptr == NULL)
|
|
|
|
|
{
|
|
|
|
|
freeMagic(uniquenamelist);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-05-21 22:33:20 +02:00
|
|
|
if (devnameptr) *devnameptr = locdname;
|
|
|
|
|
if (subnameptr) *subnameptr = devptr->exts_deviceSubstrateName;
|
|
|
|
|
if (devtypeptr) *devtypeptr = t;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
tmask = &devptr->exts_deviceSDTypes[0];
|
2021-05-21 22:33:20 +02:00
|
|
|
if (s_rclassptr)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-21 22:33:20 +02:00
|
|
|
*s_rclassptr = (short)(-1); /* NO_RESCLASS */
|
|
|
|
|
|
|
|
|
|
for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-21 22:33:20 +02:00
|
|
|
rmask = &ExtCurStyle->exts_typesByResistClass[n];
|
|
|
|
|
if (TTMaskIntersect(rmask, tmask))
|
|
|
|
|
{
|
|
|
|
|
*s_rclassptr = (short)n;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 22:33:20 +02:00
|
|
|
if (d_rclassptr)
|
2020-09-11 23:29:12 +02:00
|
|
|
{
|
2021-05-21 22:33:20 +02:00
|
|
|
tmask = &devptr->exts_deviceSDTypes[1];
|
|
|
|
|
if (TTMaskIsZero(tmask))
|
|
|
|
|
{
|
|
|
|
|
/* Set source and drain resistance classes to be the same */
|
|
|
|
|
*d_rclassptr = (short)n;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*d_rclassptr = (short)(-1); /* NO_RESCLASS */
|
|
|
|
|
|
|
|
|
|
for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
|
|
|
|
|
{
|
|
|
|
|
rmask = &ExtCurStyle->exts_typesByResistClass[n];
|
|
|
|
|
if (TTMaskIntersect(rmask, tmask))
|
|
|
|
|
{
|
|
|
|
|
*d_rclassptr = (short)n;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-11 23:29:12 +02:00
|
|
|
}
|
2021-05-21 22:33:20 +02:00
|
|
|
|
|
|
|
|
if (sub_rclassptr)
|
2020-09-11 23:29:12 +02:00
|
|
|
{
|
2021-05-21 22:33:20 +02:00
|
|
|
tmask = &devptr->exts_deviceSubstrateTypes;
|
|
|
|
|
*sub_rclassptr = (short)(-1); /* NO_RESCLASS */
|
2020-09-11 23:29:12 +02:00
|
|
|
|
|
|
|
|
for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
|
|
|
|
|
{
|
|
|
|
|
rmask = &ExtCurStyle->exts_typesByResistClass[n];
|
|
|
|
|
if (TTMaskIntersect(rmask, tmask))
|
|
|
|
|
{
|
2021-05-21 22:33:20 +02:00
|
|
|
*sub_rclassptr = (short)(n);
|
2020-09-11 23:29:12 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
freeMagic(uniquenamelist);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-21 04:12:02 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* extGetDevType --
|
|
|
|
|
*
|
|
|
|
|
* Given an extraction model device name (devname), return the associated
|
|
|
|
|
* magic tiletype for the device.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Tile type that represents the device "devname" in the magic database.
|
|
|
|
|
*
|
2021-07-27 20:34:01 +02:00
|
|
|
* Notes:
|
|
|
|
|
* It is possible for the extract section to define multiple tile types
|
|
|
|
|
* that produce the same extracted device name, so the returned TileType
|
|
|
|
|
* is not guaranteed to be unique to the device name.
|
|
|
|
|
*
|
2019-10-21 04:12:02 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
TileType
|
|
|
|
|
extGetDevType(devname)
|
|
|
|
|
char *devname;
|
|
|
|
|
{
|
|
|
|
|
TileType t;
|
|
|
|
|
ExtDevice *devptr;
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next)
|
|
|
|
|
if (!strcmp(devptr->exts_deviceName, devname))
|
|
|
|
|
return t;
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 22:26:24 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtGetGateTypesMask --
|
|
|
|
|
*
|
|
|
|
|
* Put a mask of FET gate types in the mask pointer argument.
|
|
|
|
|
* Return 0 on success, 1 on failure (no extraction style set)
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ExtGetGateTypesMask(mask)
|
|
|
|
|
TileTypeBitMask *mask;
|
|
|
|
|
{
|
|
|
|
|
TileType ttype;
|
|
|
|
|
|
|
|
|
|
if (ExtCurStyle == NULL) return 1;
|
|
|
|
|
|
|
|
|
|
TTMaskZero(mask);
|
|
|
|
|
for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, ttype))
|
|
|
|
|
{
|
|
|
|
|
ExtDevice *devptr = ExtCurStyle->exts_device[ttype];
|
|
|
|
|
|
|
|
|
|
for (; devptr; devptr = devptr->exts_next)
|
|
|
|
|
if ((devptr->exts_deviceClass == DEV_MOSFET) ||
|
|
|
|
|
(devptr->exts_deviceClass == DEV_FET) ||
|
|
|
|
|
(devptr->exts_deviceClass == DEV_ASYMMETRIC) ||
|
|
|
|
|
(devptr->exts_deviceClass == DEV_MSUBCKT))
|
|
|
|
|
TTMaskSetType(mask, ttype);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtGetDiffTypesMask --
|
|
|
|
|
*
|
|
|
|
|
* Put a mask of diffusion types in the mask pointer argument.
|
|
|
|
|
* Return 0 on success, 1 on failure (no extraction style set)
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ExtGetDiffTypesMask(mask)
|
|
|
|
|
TileTypeBitMask *mask;
|
|
|
|
|
{
|
|
|
|
|
TileType ttype;
|
|
|
|
|
|
|
|
|
|
if (ExtCurStyle == NULL) return 1;
|
|
|
|
|
|
|
|
|
|
TTMaskZero(mask);
|
|
|
|
|
TTMaskSetMask(mask, &ExtCurStyle->exts_antennaTieTypes);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#ifdef THREE_D
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtGetZAxis --
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Get the height and thickness parameters for a layer (used by the
|
|
|
|
|
* graphics module which does not have access to internal variables
|
|
|
|
|
* in the extract section).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Fills values "height" and "thick" in argument list.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtGetZAxis(tile, height, thick)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
float *height, *thick;
|
|
|
|
|
{
|
|
|
|
|
TileType ttype;
|
|
|
|
|
|
|
|
|
|
if (ExtCurStyle == NULL) return;
|
|
|
|
|
|
|
|
|
|
ttype = TiGetLeftType(tile); /* Ignore non-Manhattan for now */
|
|
|
|
|
|
|
|
|
|
/* Note that w_scale is multiplied by SUBPIXEL to get fixed-point accuracy. */
|
|
|
|
|
/* However, we downshift by only 9 (divide by 512) so that heights are */
|
|
|
|
|
/* exaggerated in the layout by a factor of 8 (height exaggeration is */
|
|
|
|
|
/* standard practice for VLSI cross-sections). */
|
|
|
|
|
|
|
|
|
|
*height = ExtCurStyle->exts_height[ttype];
|
|
|
|
|
*thick = ExtCurStyle->exts_thick[ttype];
|
|
|
|
|
}
|
|
|
|
|
#endif /* THREE_D */
|
|
|
|
|
|
2023-06-14 19:55:59 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtPrintPath --
|
|
|
|
|
*
|
|
|
|
|
* Print the path where extraction files will be written
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Output.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtPrintPath(dolist)
|
|
|
|
|
bool dolist;
|
|
|
|
|
{
|
|
|
|
|
if (ExtLocalPath == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_SetObjResult(magicinterp, Tcl_NewStringObj("(none)", -1));
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("(none)\n");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("(none)\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_SetObjResult(magicinterp, Tcl_NewStringObj(ExtLocalPath, -1));
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s\n", ExtLocalPath);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("The extraction path is: %s\n", ExtLocalPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtSetPath --
|
|
|
|
|
*
|
|
|
|
|
* Set the current extraction path to 'path', unless 'path' is "none" or
|
|
|
|
|
* "(none)", in which case clear the path and set it to NULL.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Just told you.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtSetPath(path)
|
|
|
|
|
char *path;
|
|
|
|
|
{
|
|
|
|
|
if (path != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (!strcasecmp(path, "none") || !strcasecmp(path, "(none)") ||
|
|
|
|
|
!strcasecmp(path, "null"))
|
|
|
|
|
{
|
|
|
|
|
StrDup(&ExtLocalPath, NULL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
StrDup(&ExtLocalPath, path);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtPrintStyle --
|
|
|
|
|
*
|
|
|
|
|
* Print the available and/or current extraction styles.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Output.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtPrintStyle(dolist, doforall, docurrent)
|
|
|
|
|
bool dolist;
|
|
|
|
|
bool doforall;
|
|
|
|
|
bool docurrent;
|
|
|
|
|
{
|
|
|
|
|
ExtKeep *style;
|
|
|
|
|
|
|
|
|
|
if (docurrent)
|
|
|
|
|
{
|
|
|
|
|
if (ExtCurStyle == NULL)
|
|
|
|
|
TxError("Error: No style is set\n");
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!dolist) TxPrintf("The current style is \"");
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
if (dolist)
|
2025-01-06 17:12:11 +01:00
|
|
|
Tcl_SetResult(magicinterp, (char *) ExtCurStyle->exts_name, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
TxPrintf("%s", ExtCurStyle->exts_name);
|
|
|
|
|
if (!dolist) TxPrintf("\".\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (doforall)
|
|
|
|
|
{
|
|
|
|
|
if (!dolist) TxPrintf("The extraction styles are: ");
|
|
|
|
|
|
|
|
|
|
for (style = ExtAllStyles; style; style = style->exts_next)
|
|
|
|
|
{
|
|
|
|
|
if (dolist)
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, style->exts_name);
|
|
|
|
|
#else
|
|
|
|
|
if (style != ExtAllStyles) TxPrintf(" ");
|
|
|
|
|
TxPrintf("%s", style->exts_name);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (style != ExtAllStyles) TxPrintf(", ");
|
|
|
|
|
TxPrintf("%s", style->exts_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!dolist) TxPrintf(".\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtSetStyle --
|
|
|
|
|
*
|
|
|
|
|
* Set the current extraction style to 'name', or print
|
|
|
|
|
* the available and current styles if 'name' is NULL.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Just told you.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtSetStyle(name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
ExtKeep *style, *match;
|
|
|
|
|
int length;
|
|
|
|
|
|
|
|
|
|
if (name == NULL) return;
|
|
|
|
|
|
|
|
|
|
match = NULL;
|
|
|
|
|
length = strlen(name);
|
|
|
|
|
for (style = ExtAllStyles; style; style = style->exts_next)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp(name, style->exts_name, length) == 0)
|
|
|
|
|
{
|
|
|
|
|
if (match != NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Extraction style \"%s\" is ambiguous.\n", name);
|
|
|
|
|
ExtPrintStyle(FALSE, TRUE, TRUE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
match = style;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (match != NULL)
|
|
|
|
|
{
|
|
|
|
|
ExtLoadStyle(match->exts_name);
|
|
|
|
|
TxPrintf("Extraction style is now \"%s\"\n", name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TxError("\"%s\" is not one of the extraction styles Magic knows.\n", name);
|
|
|
|
|
ExtPrintStyle(FALSE, TRUE, TRUE);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* extTechStyleAlloc --
|
|
|
|
|
*
|
|
|
|
|
* Allocate memory for a new extract style structure.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ExtStyle *
|
|
|
|
|
extTechStyleAlloc()
|
|
|
|
|
{
|
|
|
|
|
ExtStyle *style;
|
|
|
|
|
TileType r;
|
|
|
|
|
|
|
|
|
|
style = (ExtStyle *) mallocMagic(sizeof (ExtStyle));
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* extTechStyleInit --
|
|
|
|
|
*
|
|
|
|
|
* Fill in the extract style structure with initial values.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
extTechStyleInit(style)
|
|
|
|
|
ExtStyle *style;
|
|
|
|
|
{
|
|
|
|
|
TileType r, s;
|
|
|
|
|
|
|
|
|
|
style->exts_name = NULL;
|
|
|
|
|
style->exts_status = TECH_NOT_LOADED;
|
|
|
|
|
|
|
|
|
|
style->exts_sidePlanes = style->exts_overlapPlanes = 0;
|
2019-08-19 20:11:02 +02:00
|
|
|
TTMaskZero(&style->exts_deviceMask);
|
2017-04-25 14:41:48 +02:00
|
|
|
style->exts_activeTypes = DBAllButSpaceAndDRCBits;
|
|
|
|
|
|
|
|
|
|
for (r = 0; r < NP; r++)
|
|
|
|
|
{
|
|
|
|
|
TTMaskZero(&style->exts_sideTypes[r]);
|
|
|
|
|
TTMaskZero(&style->exts_overlapTypes[r]);
|
|
|
|
|
style->exts_planeOrder[r] = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (r = 0; r < NT; r++)
|
|
|
|
|
{
|
|
|
|
|
TTMaskZero(&style->exts_nodeConn[r]);
|
|
|
|
|
TTMaskZero(&style->exts_resistConn[r]);
|
2019-08-19 20:11:02 +02:00
|
|
|
TTMaskZero(&style->exts_deviceConn[r]);
|
2017-04-25 14:41:48 +02:00
|
|
|
style->exts_allConn[r] = DBAllTypeBits;
|
|
|
|
|
|
|
|
|
|
style->exts_sheetResist[r] = 0;
|
|
|
|
|
style->exts_cornerChop[r] = 1.0;
|
2022-02-23 21:02:40 +01:00
|
|
|
style->exts_viaResist[r] = (ResValue) 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
style->exts_height[r] = 0.0;
|
|
|
|
|
style->exts_thick[r] = 0.0;
|
|
|
|
|
style->exts_areaCap[r] = (CapValue) 0;
|
|
|
|
|
style->exts_overlapOtherPlanes[r] = 0;
|
|
|
|
|
TTMaskZero(&style->exts_overlapOtherTypes[r]);
|
|
|
|
|
TTMaskZero(&style->exts_sideEdges[r]);
|
|
|
|
|
for (s = 0; s < NT; s++)
|
|
|
|
|
{
|
|
|
|
|
TTMaskZero(&style->exts_sideCoupleOtherEdges[r][s]);
|
|
|
|
|
TTMaskZero(&style->exts_sideOverlapOtherTypes[r][s]);
|
|
|
|
|
style->exts_sideOverlapOtherPlanes[r][s] = 0;
|
|
|
|
|
style->exts_sideCoupleCap[r][s] = (EdgeCap *) NULL;
|
|
|
|
|
style->exts_sideOverlapCap[r][s] = (EdgeCap *) NULL;
|
|
|
|
|
style->exts_perimCap[r][s] = (CapValue) 0;
|
|
|
|
|
style->exts_overlapCap[r][s] = (CapValue) 0;
|
2023-01-27 17:47:37 +01:00
|
|
|
style->exts_overlapMult[r][s] = (float) 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
TTMaskZero(&style->exts_overlapShieldTypes[r][s]);
|
|
|
|
|
style->exts_overlapShieldPlanes[r][s] = 0;
|
|
|
|
|
style->exts_sideOverlapShieldPlanes[r][s] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TTMaskZero(&style->exts_perimCapMask[r]);
|
|
|
|
|
#ifdef ARIEL
|
|
|
|
|
TTMaskZero(&style->exts_subsTransistorTypes[r]);
|
|
|
|
|
#endif
|
2019-08-19 20:11:02 +02:00
|
|
|
if (style->exts_device[r] != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
ExtDevice *devptr;
|
|
|
|
|
for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
|
|
|
|
|
if (devptr->exts_deviceSDTypes != NULL)
|
|
|
|
|
freeMagic(devptr->exts_deviceSDTypes);
|
|
|
|
|
if (devptr->exts_deviceSubstrateName != (char *) NULL)
|
|
|
|
|
freeMagic(devptr->exts_deviceSubstrateName);
|
|
|
|
|
if (devptr->exts_deviceName != (char *) NULL)
|
|
|
|
|
freeMagic(devptr->exts_deviceName);
|
|
|
|
|
while (devptr->exts_deviceParams != (ParamList *) NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Parameter lists are shared. Only free the last one! */
|
|
|
|
|
|
|
|
|
|
if (devptr->exts_deviceParams->pl_count > 1)
|
|
|
|
|
{
|
|
|
|
|
devptr->exts_deviceParams->pl_count--;
|
|
|
|
|
devptr->exts_deviceParams = (ParamList *)NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-07-06 18:35:47 +02:00
|
|
|
if (devptr->exts_deviceParams->pl_name != NULL)
|
|
|
|
|
freeMagic(devptr->exts_deviceParams->pl_name);
|
2019-08-19 20:11:02 +02:00
|
|
|
freeMagic(devptr->exts_deviceParams);
|
|
|
|
|
devptr->exts_deviceParams = devptr->exts_deviceParams->pl_next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (devptr->exts_deviceResist.ht_table != (HashEntry **) NULL)
|
|
|
|
|
HashKill(&devptr->exts_deviceResist);
|
|
|
|
|
|
|
|
|
|
freeMagic(devptr);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-08-19 20:11:02 +02:00
|
|
|
style->exts_device[r] = (ExtDevice *)NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
style->exts_sideCoupleHalo = 0;
|
|
|
|
|
style->exts_stepSize = 100;
|
|
|
|
|
style->exts_unitsPerLambda = 100.0;
|
|
|
|
|
style->exts_resistScale = 1000;
|
|
|
|
|
style->exts_capScale = 1000;
|
|
|
|
|
style->exts_numResistClasses = 0;
|
|
|
|
|
|
|
|
|
|
style->exts_planeOrderStatus = needPlaneOrder ;
|
2019-10-18 20:12:52 +02:00
|
|
|
TTMaskZero(&style->exts_antennaTieTypes);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
for (r = 0; r < DBNumTypes; r++)
|
|
|
|
|
{
|
2019-11-27 16:38:47 +01:00
|
|
|
style->exts_antennaRatio[r].areaType = (char)0;
|
2019-10-21 04:12:02 +02:00
|
|
|
style->exts_antennaRatio[r].ratioGate = 0.0;
|
2019-11-27 16:38:47 +01:00
|
|
|
style->exts_antennaRatio[r].ratioDiffA = 0.0;
|
|
|
|
|
style->exts_antennaRatio[r].ratioDiffB = 0.0;
|
2022-02-23 21:02:40 +01:00
|
|
|
style->exts_resistByResistClass[r] = (ResValue) 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskZero(&style->exts_typesByResistClass[r]);
|
|
|
|
|
style->exts_typesResistChanged[r] = DBAllButSpaceAndDRCBits;
|
|
|
|
|
TTMaskSetType(&style->exts_typesResistChanged[r], TT_SPACE);
|
|
|
|
|
style->exts_typeToResistClass[r] = -1;
|
2019-10-18 20:12:52 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
doConvert = FALSE;
|
|
|
|
|
|
|
|
|
|
// The exts_globSubstratePlane setting of -1 will be used to set a
|
|
|
|
|
// backwards-compatibility mode matching previous behavior with
|
|
|
|
|
// respect to the substrate when there is no "substrate" line in
|
|
|
|
|
// the techfile.
|
|
|
|
|
|
|
|
|
|
style->exts_globSubstratePlane = -1;
|
2022-02-23 21:02:40 +01:00
|
|
|
style->exts_globSubstrateDefaultType = -1;
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskZero(&style->exts_globSubstrateTypes);
|
2019-09-19 02:48:33 +02:00
|
|
|
TTMaskZero(&style->exts_globSubstrateShieldTypes);
|
2020-06-01 22:49:59 +02:00
|
|
|
style->exts_globSubstrateName = (char *)NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* extTechStyleNew --
|
|
|
|
|
*
|
|
|
|
|
* Allocate a new style with zeroed technology variables.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Allocates a new ExtStyle, initializes it, and returns it.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ExtStyle *
|
|
|
|
|
extTechStyleNew()
|
|
|
|
|
{
|
|
|
|
|
ExtStyle *style;
|
2020-06-05 18:46:46 +02:00
|
|
|
TileType r;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
style = extTechStyleAlloc();
|
2020-06-05 18:46:46 +02:00
|
|
|
|
|
|
|
|
/* This entry in ExtStyle is allocated and must be initialized */
|
|
|
|
|
for (r = 0; r < NT; r++)
|
|
|
|
|
style->exts_device[r] = NULL;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
extTechStyleInit(style);
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* aToCap --
|
|
|
|
|
*
|
|
|
|
|
* Utility procedure for reading capacitance values.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* A value of type CapValue.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* none.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
CapValue
|
|
|
|
|
aToCap(str)
|
|
|
|
|
char *str;
|
|
|
|
|
{
|
|
|
|
|
CapValue capVal;
|
|
|
|
|
if (sscanf(str, "%lf", &capVal) != 1) {
|
|
|
|
|
capVal = (CapValue) 0;
|
|
|
|
|
TechError("Capacitance value %s must be a number\n", str);
|
|
|
|
|
}
|
|
|
|
|
return capVal;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-23 21:02:40 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* aToRes --
|
|
|
|
|
*
|
|
|
|
|
* Utility procedure for reading resistance values.
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* A value of type ResValue.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* none.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ResValue
|
|
|
|
|
aToRes(str)
|
|
|
|
|
char *str;
|
|
|
|
|
{
|
|
|
|
|
ResValue resVal;
|
|
|
|
|
if (sscanf(str, "%d", &resVal) != 1) {
|
|
|
|
|
resVal = (ResValue) 0;
|
|
|
|
|
TechError("Resistance value %s must be a number\n", str);
|
|
|
|
|
}
|
|
|
|
|
return resVal;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtLoadStyle --
|
|
|
|
|
*
|
|
|
|
|
* Re-read the technology file to load the specified technology extraction
|
|
|
|
|
* style into structure ExtCurStyle. This is much more memory-efficient than
|
|
|
|
|
* keeping a separate (and huge!) ExtStyle structure for each extraction style.
|
2020-05-23 23:13:14 +02:00
|
|
|
* It incurs a complete reading of the tech file on startup and every time the
|
2017-04-25 14:41:48 +02:00
|
|
|
* extraction style is changed, but we can assume that this does not happen
|
|
|
|
|
* often. The first style in the technology file is assumed to be default, so
|
|
|
|
|
* that re-reading the tech file is not necessary on startup unless the default
|
|
|
|
|
* extraction style is changed by a call to "extract style".
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
ExtLoadStyle(stylename)
|
|
|
|
|
char *stylename;
|
|
|
|
|
{
|
|
|
|
|
SectionID invext;
|
|
|
|
|
|
|
|
|
|
extTechStyleInit(ExtCurStyle); /* Reinitialize and mark as not */
|
|
|
|
|
ExtCurStyle->exts_name = stylename; /* loaded. */
|
|
|
|
|
|
|
|
|
|
/* Invalidate the extract section, and reload it. */
|
|
|
|
|
/* The second parameter to TechSectionGetMask is NULL because */
|
|
|
|
|
/* no other tech client sections depend on the extract section. */
|
|
|
|
|
|
|
|
|
|
invext = TechSectionGetMask("extract", NULL);
|
2021-08-23 19:46:19 +02:00
|
|
|
|
|
|
|
|
/* If microns are used as units, then the TechLoad needs to convert */
|
|
|
|
|
/* units based on *unscaled* dimensions. Since it gets the scale */
|
|
|
|
|
/* factor from CIFGetOutputScale(), the CIF units need to be */
|
|
|
|
|
/* unscaled. This is cumbersome but ensures that the right units */
|
|
|
|
|
/* are obtained. */
|
|
|
|
|
CIFTechOutputScale(DBLambda[1], DBLambda[0]);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
TechLoad(NULL, invext);
|
|
|
|
|
|
2021-08-23 19:46:19 +02:00
|
|
|
/* Put the CIF output scale units back to what they were */
|
|
|
|
|
CIFTechOutputScale(DBLambda[0], DBLambda[1]);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* extTechFinalStyle(ExtCurStyle); */ /* Taken care of by TechLoad() */
|
|
|
|
|
ExtTechScale(DBLambda[0], DBLambda[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechInit --
|
|
|
|
|
*
|
|
|
|
|
* Ensure that all memory allocated to the extract database has
|
|
|
|
|
* been freed up. Called before loading a new technology.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
ExtTechInit()
|
|
|
|
|
{
|
|
|
|
|
ExtKeep *style;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
/* Delete everything in ExtCurStyle */
|
|
|
|
|
|
|
|
|
|
if (ExtCurStyle != NULL)
|
|
|
|
|
{
|
|
|
|
|
extTechStyleInit(ExtCurStyle);
|
|
|
|
|
ExtCurStyle = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Forget all the extract style names */
|
|
|
|
|
|
|
|
|
|
for (style = ExtAllStyles; style != NULL; style = style->exts_next)
|
|
|
|
|
{
|
|
|
|
|
freeMagic(style->exts_name);
|
|
|
|
|
freeMagic(style);
|
|
|
|
|
}
|
|
|
|
|
ExtAllStyles = NULL;
|
2022-01-14 23:30:05 +01:00
|
|
|
|
2023-02-22 23:56:45 +01:00
|
|
|
if (allExtractTypes == NULL)
|
|
|
|
|
allExtractTypes = (TileTypeBitMask *)mallocMagic(sizeof(TileTypeBitMask));
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskZero(allExtractTypes);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechSimpleAreaCap --
|
|
|
|
|
*
|
|
|
|
|
* Parse the techfile line for the "defaultareacap" keyword.
|
|
|
|
|
* This is equivalent to the "areacap" line but also applies
|
|
|
|
|
* to "overlap" of types on the second plane (if specified) and
|
2020-05-23 23:13:14 +02:00
|
|
|
* all planes below it, with appropriate intervening types.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* defaultareacap types plane [[subtypes] subplane] value
|
|
|
|
|
*
|
|
|
|
|
* where "types" are the types for which to compute area cap,
|
|
|
|
|
* "plane" is the plane of "types", "subplane" is a plane
|
|
|
|
|
* containing wells or any types that have the same coupling
|
|
|
|
|
* as the substrate. If absent, it is assumed that nothing
|
|
|
|
|
* shields "types" from the subtrate. Additional optional
|
|
|
|
|
* "subtypes" is a list of types in "subplane" that shield.
|
|
|
|
|
* If absent, then all types in "subplane" are shields to the
|
2021-12-31 17:13:59 +01:00
|
|
|
* substrate. All types specified in the "substrate" statement
|
|
|
|
|
* as shielding substrate are automatically included. "value"
|
|
|
|
|
* is the area capacitance in aF/um^2.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Adds information into the ExtCurStyle records.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtTechSimpleAreaCap(argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
TileType s, t;
|
|
|
|
|
TileTypeBitMask types, subtypes, shields;
|
|
|
|
|
CapValue capVal;
|
2023-01-27 17:47:37 +01:00
|
|
|
float multVal;
|
2017-04-25 14:41:48 +02:00
|
|
|
int plane1, plane2, plane3, pnum1, pnum2, pnum3;
|
|
|
|
|
PlaneMask pshield;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TechError("Cannot parse area cap line without plane ordering!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[1], &types);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types);
|
2017-04-25 14:41:48 +02:00
|
|
|
plane1 = DBTechNoisyNamePlane(argv[2]);
|
|
|
|
|
TTMaskAndMask(&types, &DBPlaneTypes[plane1]);
|
|
|
|
|
|
|
|
|
|
capVal = aToCap(argv[argc - 1]);
|
|
|
|
|
|
|
|
|
|
if (argc == 4)
|
2022-02-23 21:02:40 +01:00
|
|
|
plane2 = ExtCurStyle->exts_globSubstratePlane;
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
plane2 = DBTechNoisyNamePlane(argv[argc - 2]);
|
|
|
|
|
|
|
|
|
|
if (argc > 5)
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[argc - 3], &subtypes);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subtypes);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2021-12-31 17:13:59 +01:00
|
|
|
TTMaskZero(&subtypes);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Part 1: Area cap */
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types, t))
|
2023-01-27 17:47:37 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_areaCap[t] = capVal;
|
2023-01-27 17:47:37 +01:00
|
|
|
ExtCurStyle->exts_overlapMult[t][0] = (float) capVal * FRINGE_MULT;
|
|
|
|
|
ExtCurStyle->exts_overlapMult[0][t] = (float) capVal * FRINGE_MULT;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-12-31 17:13:59 +01:00
|
|
|
if ((plane2 == -1) && (ExtCurStyle->exts_globSubstratePlane == -1)) return;
|
2017-04-25 14:41:48 +02:00
|
|
|
else if (plane1 == plane2) return; /* shouldn't happen */
|
|
|
|
|
|
|
|
|
|
pnum1 = ExtCurStyle->exts_planeOrder[plane1];
|
2021-12-31 17:13:59 +01:00
|
|
|
if (plane2 != -1)
|
|
|
|
|
pnum2 = ExtCurStyle->exts_planeOrder[plane2];
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Part 2: Overlap cap on types equivalent to substrate */
|
|
|
|
|
/* Find all types in or below plane2 (i.e., ~(space)/plane2) */
|
|
|
|
|
/* Shield types are everything in the planes between plane1 and plane2 */
|
|
|
|
|
|
|
|
|
|
TTMaskZero(&shields);
|
|
|
|
|
pshield = 0;
|
2021-12-31 17:13:59 +01:00
|
|
|
|
|
|
|
|
if (plane2 != -1)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-12-31 17:13:59 +01:00
|
|
|
for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-12-31 17:13:59 +01:00
|
|
|
pnum3 = ExtCurStyle->exts_planeOrder[plane3];
|
|
|
|
|
if (pnum3 > pnum2 && pnum3 < pnum1)
|
|
|
|
|
{
|
|
|
|
|
TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
|
|
|
|
|
pshield |= PlaneNumToMaskBit(plane3);
|
|
|
|
|
}
|
|
|
|
|
else if (pnum3 <= pnum2)
|
|
|
|
|
{
|
|
|
|
|
TTMaskAndMask(&subtypes, &DBPlaneTypes[plane3]);
|
|
|
|
|
TTMaskClearType(&subtypes, TT_SPACE);
|
|
|
|
|
}
|
|
|
|
|
TTMaskClearType(&shields, TT_SPACE);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-23 21:02:40 +01:00
|
|
|
/* Defaults from the "substrate" line, if used, and if the arguments */
|
|
|
|
|
/* do not specify the plane and shielding types. */
|
|
|
|
|
|
|
|
|
|
if ((ExtCurStyle->exts_globSubstratePlane != -1) && (argc == 4))
|
|
|
|
|
{
|
|
|
|
|
TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes);
|
|
|
|
|
TTMaskClearMask(&subtypes, &ExtCurStyle->exts_globSubstrateShieldTypes);
|
|
|
|
|
TTMaskClearType(&subtypes, TT_SPACE);
|
|
|
|
|
|
|
|
|
|
/* If the substrate type is used for isolated substrate regions, */
|
|
|
|
|
/* then the substrate plane is also a shielding plane, and the */
|
|
|
|
|
/* default substrate type is a shielding type. */
|
|
|
|
|
|
|
|
|
|
if (ExtCurStyle->exts_globSubstrateDefaultType != -1)
|
|
|
|
|
{
|
|
|
|
|
pshield |= PlaneNumToMaskBit(ExtCurStyle->exts_globSubstratePlane);
|
|
|
|
|
|
|
|
|
|
/* The substrate default type now marks isolated regions */
|
|
|
|
|
/* and so is not itself a substrate type. */
|
|
|
|
|
|
|
|
|
|
TTMaskClearType(&subtypes, ExtCurStyle->exts_globSubstrateDefaultType);
|
|
|
|
|
|
|
|
|
|
/* All types on the substrate plane that are not substrate */
|
|
|
|
|
/* are by definition shielding types. */
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (DBPlane(t) == ExtCurStyle->exts_globSubstratePlane)
|
|
|
|
|
if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, t))
|
|
|
|
|
TTMaskSetType(&shields, t);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-31 17:13:59 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Now record all of the overlap capacitances */
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&types, s))
|
|
|
|
|
{
|
|
|
|
|
/* Contact overlap caps are determined from residues */
|
|
|
|
|
if (DBIsContact(s)) continue;
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
2022-02-23 21:02:40 +01:00
|
|
|
if (!TTMaskHasType(&subtypes, t))
|
|
|
|
|
continue;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (s == t) continue; /* shouldn't happen */
|
|
|
|
|
if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0)
|
|
|
|
|
continue; /* redundant overlap */
|
|
|
|
|
|
|
|
|
|
ExtCurStyle->exts_overlapCap[s][t] = capVal;
|
2023-01-27 17:47:37 +01:00
|
|
|
ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT;
|
|
|
|
|
ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT;
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(plane1);
|
2021-12-31 17:13:59 +01:00
|
|
|
if (plane2 != -1)
|
|
|
|
|
ExtCurStyle->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(plane2);
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskSetType(&ExtCurStyle->exts_overlapTypes[plane1], s);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t);
|
|
|
|
|
|
|
|
|
|
ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield;
|
|
|
|
|
ExtCurStyle->exts_overlapShieldTypes[s][t] = shields;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechSimplePerimCap --
|
|
|
|
|
*
|
|
|
|
|
* Parse the techfile line for the "defaultperimeter" keyword.
|
|
|
|
|
* This comprises both the "perimc" statement and the "sideoverlap"
|
|
|
|
|
* statement for overlaps to types that are effectively substrate
|
|
|
|
|
* (e.g., well, implant, marker layers, etc.)
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* defaultperimeter types plane [[subtypes] subplane] value
|
|
|
|
|
*
|
|
|
|
|
* where "types" are the types for which to compute fringing cap,
|
|
|
|
|
* "plane" is the plane of the types, "subplane" is an optional
|
|
|
|
|
* plane that shields "types" from substrate, and "value" is the
|
|
|
|
|
* fringing cap in aF/micron. If "subplane" is omitted, then
|
|
|
|
|
* nothing shields "types" from the substrate. Optional "subtypes"
|
|
|
|
|
* lists the types in "subplane" that shield. Otherwise, it is
|
|
|
|
|
* assumed that all types in "subplane" shield "types" from the
|
2021-12-31 17:13:59 +01:00
|
|
|
* substrate. Additionally, all types declared to shield the
|
|
|
|
|
* substrate are included.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Adds information into the ExtCurStyle records.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtTechSimplePerimCap(argc, argv)
|
|
|
|
|
int argc;
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
TileType r, s, t;
|
|
|
|
|
TileTypeBitMask types, nottypes, subtypes, shields;
|
|
|
|
|
CapValue capVal;
|
|
|
|
|
int plane1, plane2, plane3, pnum1, pnum2, pnum3;
|
|
|
|
|
PlaneMask pshield;
|
|
|
|
|
EdgeCap *cnew;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TechError("Cannot parse area cap line without plane ordering!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[1], &types);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types);
|
2017-04-25 14:41:48 +02:00
|
|
|
plane1 = DBTechNoisyNamePlane(argv[2]);
|
2022-02-24 02:57:30 +01:00
|
|
|
|
|
|
|
|
TTMaskCom2(¬types, &types);
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskAndMask(&types, &DBPlaneTypes[plane1]);
|
|
|
|
|
TTMaskAndMask(¬types, &DBPlaneTypes[plane1]);
|
|
|
|
|
|
|
|
|
|
capVal = aToCap(argv[argc - 1]);
|
|
|
|
|
|
2022-02-23 21:02:40 +01:00
|
|
|
if (argc == 4)
|
|
|
|
|
plane2 = ExtCurStyle->exts_globSubstratePlane;
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2022-02-23 21:02:40 +01:00
|
|
|
plane2 = DBTechNoisyNamePlane(argv[argc - 2]);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-10-23 04:12:15 +02:00
|
|
|
if (argc > 5)
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[argc - 3], &subtypes);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subtypes);
|
|
|
|
|
}
|
2022-02-24 02:57:30 +01:00
|
|
|
else if (ExtCurStyle->exts_globSubstratePlane != -1)
|
|
|
|
|
{
|
2021-12-31 17:13:59 +01:00
|
|
|
TTMaskZero(&subtypes);
|
2022-02-24 02:57:30 +01:00
|
|
|
TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TTMaskSetOnlyType(&subtypes, TT_SPACE);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Part 1: Perimeter cap */
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
for (t = 0; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types, s) && TTMaskHasType(¬types, t))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_perimCap[s][t] = capVal;
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_perimCapMask[s], t);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-31 17:13:59 +01:00
|
|
|
if ((plane2 == -1) && (ExtCurStyle->exts_globSubstratePlane == -1)) return;
|
2017-04-25 14:41:48 +02:00
|
|
|
else if (plane1 == plane2) return; /* shouldn't happen */
|
|
|
|
|
|
|
|
|
|
pnum1 = ExtCurStyle->exts_planeOrder[plane1];
|
2021-12-31 17:13:59 +01:00
|
|
|
if (plane2 != -1)
|
|
|
|
|
pnum2 = ExtCurStyle->exts_planeOrder[plane2];
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Part 2: Sidewall overlap cap on types equivalent to substrate */
|
|
|
|
|
/* Find all types in or below plane2 (i.e., ~(space)/plane2) */
|
|
|
|
|
/* Shield types are everything in the planes between plane1 and plane2 */
|
|
|
|
|
|
|
|
|
|
TTMaskZero(&shields);
|
|
|
|
|
pshield = 0;
|
2021-12-31 17:13:59 +01:00
|
|
|
|
|
|
|
|
if (plane2 != -1)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-12-31 17:13:59 +01:00
|
|
|
for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-12-31 17:13:59 +01:00
|
|
|
pnum3 = ExtCurStyle->exts_planeOrder[plane3];
|
|
|
|
|
if (pnum3 > pnum2 && pnum3 < pnum1)
|
|
|
|
|
{
|
|
|
|
|
TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
|
|
|
|
|
pshield |= PlaneNumToMaskBit(plane3);
|
|
|
|
|
}
|
|
|
|
|
else if (pnum3 <= pnum2)
|
|
|
|
|
{
|
|
|
|
|
TTMaskAndMask(&subtypes, &DBPlaneTypes[plane3]);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-12-31 17:13:59 +01:00
|
|
|
TTMaskClearType(&shields, TT_SPACE);
|
|
|
|
|
TTMaskClearType(&subtypes, TT_SPACE);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-12-31 17:13:59 +01:00
|
|
|
|
2022-02-23 21:02:40 +01:00
|
|
|
/* Defaults from the "substrate" line, if used, and if the arguments */
|
|
|
|
|
/* do not specify the plane and shielding types. */
|
|
|
|
|
|
|
|
|
|
if ((ExtCurStyle->exts_globSubstratePlane != -1) && (argc == 4))
|
|
|
|
|
{
|
|
|
|
|
TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes);
|
|
|
|
|
TTMaskClearMask(&subtypes, &ExtCurStyle->exts_globSubstrateShieldTypes);
|
|
|
|
|
TTMaskClearType(&subtypes, TT_SPACE);
|
|
|
|
|
|
|
|
|
|
/* If the substrate type is used for isolated substrate regions, */
|
|
|
|
|
/* then the substrate plane is also a shielding plane, and the */
|
|
|
|
|
/* default substrate type is a shielding type. */
|
|
|
|
|
|
|
|
|
|
if (ExtCurStyle->exts_globSubstrateDefaultType != -1)
|
|
|
|
|
{
|
|
|
|
|
pshield |= PlaneNumToMaskBit(ExtCurStyle->exts_globSubstratePlane);
|
|
|
|
|
|
|
|
|
|
/* The substrate default type now marks isolated regions */
|
|
|
|
|
/* and so is not itself a substrate type. */
|
|
|
|
|
|
|
|
|
|
TTMaskClearType(&subtypes, ExtCurStyle->exts_globSubstrateDefaultType);
|
|
|
|
|
|
2022-02-24 02:57:30 +01:00
|
|
|
/* But space is */
|
|
|
|
|
TTMaskSetType(&subtypes, TT_SPACE);
|
|
|
|
|
|
2022-02-23 21:02:40 +01:00
|
|
|
/* All types on the substrate plane that are not substrate */
|
|
|
|
|
/* are by definition shielding types. */
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (DBPlane(t) == ExtCurStyle->exts_globSubstratePlane)
|
|
|
|
|
if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, t))
|
|
|
|
|
TTMaskSetType(&shields, t);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Record all of the sideoverlap capacitances */
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
/* Side overlap computed from residues */
|
|
|
|
|
if (DBIsContact(s)) continue;
|
|
|
|
|
|
|
|
|
|
if (TTMaskHasType(&types, s)) // Corrected, 2/21/2017
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(plane1);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_sideTypes[plane1], s);
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], ¬types);
|
|
|
|
|
for (t = 0; t < DBNumTypes; t++)
|
|
|
|
|
{
|
2022-02-24 02:57:30 +01:00
|
|
|
if (!TTMaskHasType(¬types, t)) continue;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (DBIsContact(t)) continue;
|
|
|
|
|
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &subtypes);
|
2021-12-31 17:13:59 +01:00
|
|
|
if (plane2 != -1)
|
|
|
|
|
ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |=
|
2017-04-25 14:41:48 +02:00
|
|
|
PlaneNumToMaskBit(plane2);
|
|
|
|
|
cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
|
|
|
|
|
cnew->ec_cap = capVal;
|
2022-05-28 16:33:21 +02:00
|
|
|
cnew->ec_offset = 0; /* No offsets on perimeter caps */
|
2017-04-25 14:41:48 +02:00
|
|
|
cnew->ec_far = shields; /* Types that shield */
|
|
|
|
|
cnew->ec_near = subtypes; /* Types we create cap with */
|
2021-12-31 17:13:59 +01:00
|
|
|
if (plane2 != -1)
|
|
|
|
|
cnew->ec_pmask = PlaneNumToMaskBit(plane2);
|
|
|
|
|
else
|
|
|
|
|
cnew->ec_pmask = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t];
|
|
|
|
|
ExtCurStyle->exts_sideOverlapCap[s][t] = cnew;
|
|
|
|
|
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
if (TTMaskHasType(&subtypes, r))
|
|
|
|
|
ExtCurStyle->exts_sideOverlapShieldPlanes[s][r] |= pshield;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechSimpleSidewallCap --
|
|
|
|
|
*
|
|
|
|
|
* Parse the techfile line for the "defaultsidewall" keyword.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Adds information into the ExtCurStyle records.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2022-05-28 16:33:21 +02:00
|
|
|
ExtTechSimpleSidewallCap(argc, argv)
|
|
|
|
|
int argc;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
/* Like ExtTechLine, but with near = types2 and far = types1 */
|
|
|
|
|
|
|
|
|
|
TileType s, t;
|
|
|
|
|
TileTypeBitMask types1, types2;
|
|
|
|
|
CapValue capVal;
|
|
|
|
|
EdgeCap *cnew;
|
2022-05-28 16:33:21 +02:00
|
|
|
int offset;
|
|
|
|
|
double doffset;
|
2017-04-25 14:41:48 +02:00
|
|
|
int plane;
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[1], &types1);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types1);
|
2017-04-25 14:41:48 +02:00
|
|
|
plane = DBTechNoisyNamePlane(argv[2]);
|
|
|
|
|
capVal = aToCap(argv[3]);
|
|
|
|
|
|
2022-05-28 16:33:21 +02:00
|
|
|
if (argc == 5)
|
|
|
|
|
{
|
|
|
|
|
/* Save a value of 1000 * the offset, which will be converted
|
|
|
|
|
* appropriately to magic units like exts->sideCoupleHalo.
|
|
|
|
|
*/
|
|
|
|
|
sscanf(argv[4], "%lg", &doffset);
|
|
|
|
|
offset = (int)(0.5 + doffset * 1000.0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
offset = 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
// Like perimeter cap, treat only space and space-like types
|
|
|
|
|
// TTMaskCom2(&types2, &types1);
|
|
|
|
|
TTMaskZero(&types2);
|
|
|
|
|
TTMaskSetType(&types2, TT_SPACE);
|
|
|
|
|
|
|
|
|
|
TTMaskAndMask(&types1, &DBPlaneTypes[plane]);
|
|
|
|
|
TTMaskAndMask(&types2, &DBPlaneTypes[plane]);
|
|
|
|
|
|
|
|
|
|
if (TTMaskHasType(&types1, TT_SPACE))
|
|
|
|
|
TechError("Can't have space on inside of edge [ignored]\n");
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&types1, s))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(DBPlane(s));
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_sideTypes[DBPlane(s)], s);
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
|
|
|
|
|
for (t = 0; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types2, t))
|
|
|
|
|
continue;
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideCoupleOtherEdges[s][t], &types1);
|
|
|
|
|
cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
|
|
|
|
|
cnew->ec_cap = capVal;
|
2022-05-28 16:33:21 +02:00
|
|
|
cnew->ec_offset = offset;
|
2017-04-25 14:41:48 +02:00
|
|
|
cnew->ec_near = types2;
|
|
|
|
|
cnew->ec_far = types1;
|
|
|
|
|
cnew->ec_next = ExtCurStyle->exts_sideCoupleCap[s][t];
|
|
|
|
|
cnew->ec_pmask = 0;
|
|
|
|
|
ExtCurStyle->exts_sideCoupleCap[s][t] = cnew;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechSimpleOverlapCap --
|
|
|
|
|
*
|
|
|
|
|
* Parse the techfile line for the "defaultoverlap" keyword.
|
|
|
|
|
* This is the same as the "overlap" statement excet that shield
|
|
|
|
|
* types are determined automatically from the planeorder.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Adds information into the ExtCurStyle records.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtTechSimpleOverlapCap(argv)
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
TileType s, t;
|
|
|
|
|
TileTypeBitMask types1, types2, shields;
|
|
|
|
|
CapValue capVal;
|
|
|
|
|
int plane1, plane2, plane3, pnum1, pnum2, pnum3;
|
|
|
|
|
PlaneMask pshield;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TechError("Cannot parse area cap line without plane ordering!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[1], &types1);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types1);
|
2017-04-25 14:41:48 +02:00
|
|
|
plane1 = DBTechNoisyNamePlane(argv[2]);
|
|
|
|
|
TTMaskAndMask(&types1, &DBPlaneTypes[plane1]);
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[3], &types2);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types2);
|
2017-04-25 14:41:48 +02:00
|
|
|
plane2 = DBTechNoisyNamePlane(argv[4]);
|
|
|
|
|
TTMaskAndMask(&types2, &DBPlaneTypes[plane2]);
|
|
|
|
|
|
|
|
|
|
capVal = aToCap(argv[5]);
|
|
|
|
|
|
|
|
|
|
pnum1 = ExtCurStyle->exts_planeOrder[plane1];
|
|
|
|
|
pnum2 = ExtCurStyle->exts_planeOrder[plane2];
|
|
|
|
|
|
|
|
|
|
/* Find all types in or below plane2 (i.e., ~(space)/plane2) */
|
|
|
|
|
/* Shield types are everything in the planes between plane1 and plane2 */
|
|
|
|
|
|
|
|
|
|
TTMaskZero(&shields);
|
|
|
|
|
|
|
|
|
|
pshield = 0;
|
|
|
|
|
for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
|
|
|
|
|
{
|
|
|
|
|
pnum3 = ExtCurStyle->exts_planeOrder[plane3];
|
|
|
|
|
if (pnum3 > pnum2 && pnum3 < pnum1)
|
|
|
|
|
{
|
|
|
|
|
TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
|
|
|
|
|
pshield |= PlaneNumToMaskBit(plane3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TTMaskClearType(&shields, TT_SPACE);
|
|
|
|
|
|
|
|
|
|
/* Now record all of the overlap capacitances */
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&types1, s))
|
|
|
|
|
{
|
|
|
|
|
/* Contact overlap caps are determined from residues */
|
|
|
|
|
if (DBIsContact(s)) continue;
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types2, t)) continue;
|
|
|
|
|
if (DBIsContact(t)) continue;
|
|
|
|
|
|
|
|
|
|
if (s == t) continue; /* shouldn't happen */
|
|
|
|
|
if (plane1 == plane2) continue; /* shouldn't happen */
|
|
|
|
|
if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0)
|
|
|
|
|
continue; /* redundant overlap */
|
|
|
|
|
|
|
|
|
|
ExtCurStyle->exts_overlapCap[s][t] = capVal;
|
2023-01-27 17:47:37 +01:00
|
|
|
ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT;
|
|
|
|
|
ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT;
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(plane1);
|
|
|
|
|
ExtCurStyle->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(plane2);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_overlapTypes[plane1], s);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t);
|
|
|
|
|
|
|
|
|
|
ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield;
|
|
|
|
|
ExtCurStyle->exts_overlapShieldTypes[s][t] = shields;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechSimpleSideOverlapCap --
|
|
|
|
|
*
|
|
|
|
|
* Parse the techfile line for the "defaultsideoverlap" keyword.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Adds information into the ExtCurStyle records.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtTechSimpleSideOverlapCap(argv)
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
TileType r, s, t;
|
|
|
|
|
TileTypeBitMask types, nottypes, ov, notov, shields;
|
|
|
|
|
CapValue capVal;
|
|
|
|
|
int plane1, plane2, plane3, pnum1, pnum2, pnum3;
|
|
|
|
|
PlaneMask pshield;
|
|
|
|
|
EdgeCap *cnew;
|
2020-06-10 16:52:54 +02:00
|
|
|
bool forward;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TechError("Cannot parse area cap line without plane ordering!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[1], &types);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types);
|
2017-04-25 14:41:48 +02:00
|
|
|
plane1 = DBTechNoisyNamePlane(argv[2]);
|
|
|
|
|
|
2022-02-24 02:57:30 +01:00
|
|
|
TTMaskCom2(¬types, &types);
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskAndMask(&types, &DBPlaneTypes[plane1]);
|
|
|
|
|
TTMaskAndMask(¬types, &DBPlaneTypes[plane1]);
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[3], &ov);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &ov);
|
2017-04-25 14:41:48 +02:00
|
|
|
plane2 = DBTechNoisyNamePlane(argv[4]);
|
|
|
|
|
|
|
|
|
|
// TTMaskCom2(¬ov, &ov);
|
|
|
|
|
TTMaskZero(¬ov);
|
|
|
|
|
TTMaskSetType(¬ov, TT_SPACE);
|
|
|
|
|
|
|
|
|
|
TTMaskAndMask(&ov, &DBPlaneTypes[plane2]);
|
|
|
|
|
TTMaskAndMask(¬ov, &DBPlaneTypes[plane2]);
|
|
|
|
|
|
|
|
|
|
capVal = aToCap(argv[5]);
|
|
|
|
|
|
|
|
|
|
pnum1 = ExtCurStyle->exts_planeOrder[plane1];
|
|
|
|
|
pnum2 = ExtCurStyle->exts_planeOrder[plane2];
|
|
|
|
|
|
2020-06-10 16:52:54 +02:00
|
|
|
if (pnum1 == pnum2)
|
|
|
|
|
{
|
|
|
|
|
TechError("Cannot have fringing capacitance between "
|
|
|
|
|
"types on the same plane\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fringing cap works both directions. Consider the case plane1 < */
|
|
|
|
|
/* plane2 as the "forward" case, and plane1 > plane2 as the */
|
|
|
|
|
/* "reverse" case. */
|
|
|
|
|
|
|
|
|
|
forward = (plane1 < plane2) ? TRUE : FALSE;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Find all types in or below plane2 (i.e., ~(space)/plane2) */
|
|
|
|
|
/* Shield planes are the ones between plane1 and plane2 */
|
|
|
|
|
|
|
|
|
|
TTMaskZero(&shields);
|
|
|
|
|
pshield = 0;
|
|
|
|
|
for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
|
|
|
|
|
{
|
|
|
|
|
pnum3 = ExtCurStyle->exts_planeOrder[plane3];
|
2022-02-24 02:57:30 +01:00
|
|
|
if ((forward == FALSE) && (pnum3 > pnum2 && pnum3 < pnum1))
|
2020-06-10 16:52:54 +02:00
|
|
|
{
|
|
|
|
|
TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
|
|
|
|
|
pshield |= PlaneNumToMaskBit(plane3);
|
|
|
|
|
}
|
2022-02-24 02:57:30 +01:00
|
|
|
else if ((forward == TRUE) && (pnum3 < pnum2 && pnum3 > pnum1))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
|
|
|
|
|
pshield |= PlaneNumToMaskBit(plane3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TTMaskClearType(&shields, TT_SPACE);
|
|
|
|
|
|
|
|
|
|
/* Now record all of the sideoverlap capacitances */
|
|
|
|
|
|
|
|
|
|
if (TTMaskHasType(&types, TT_SPACE) || TTMaskHasType(&ov, TT_SPACE))
|
|
|
|
|
{
|
|
|
|
|
TechError("Overlap types can't contain space [ignored]\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Record all of the sideoverlap capacitances */
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
/* Side overlap computed from residues */
|
|
|
|
|
if (DBIsContact(s)) continue;
|
|
|
|
|
|
|
|
|
|
if (TTMaskHasType(&types, s))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(plane1);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_sideTypes[plane1], s);
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], ¬types);
|
|
|
|
|
for (t = TT_SPACE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(¬types, t)) continue;
|
|
|
|
|
if (DBIsContact(t)) continue;
|
|
|
|
|
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &ov);
|
|
|
|
|
ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |=
|
|
|
|
|
PlaneNumToMaskBit(plane2);
|
|
|
|
|
cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
|
|
|
|
|
cnew->ec_cap = capVal;
|
2022-05-28 16:33:21 +02:00
|
|
|
cnew->ec_offset = 0; /* No offsets on overlap caps */
|
2017-04-25 14:41:48 +02:00
|
|
|
cnew->ec_far = shields; /* Types that shield */
|
|
|
|
|
cnew->ec_near = ov; /* Types we create cap with */
|
|
|
|
|
cnew->ec_pmask = PlaneNumToMaskBit(plane2);
|
|
|
|
|
cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t];
|
|
|
|
|
ExtCurStyle->exts_sideOverlapCap[s][t] = cnew;
|
|
|
|
|
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
if (TTMaskHasType(&ov, r))
|
|
|
|
|
ExtCurStyle->exts_sideOverlapShieldPlanes[s][r] |= pshield;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-10 16:52:54 +02:00
|
|
|
/* There is no "reverse case". Overlap from A->B will be different */
|
|
|
|
|
/* than B->A because it depends on the thickness of A and B. For */
|
|
|
|
|
/* the reverse case, the defaultsideoverlap statement must be */
|
|
|
|
|
/* repeated with the correct capacitance. */
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechLine --
|
|
|
|
|
*
|
|
|
|
|
* Process a line from the "extract" section of a technology file.
|
|
|
|
|
*
|
|
|
|
|
* Each line in the extract section of a technology begins
|
|
|
|
|
* with a keyword that identifies the format of the rest of
|
|
|
|
|
* the line.
|
|
|
|
|
*
|
|
|
|
|
* The following three kinds of lines are used to define the resistance
|
|
|
|
|
* and parasitic capacitance to substrate of each tile type:
|
|
|
|
|
*
|
|
|
|
|
* resist types resistance
|
|
|
|
|
* areacap types capacitance
|
|
|
|
|
* perimcap inside outside capacitance
|
|
|
|
|
*
|
|
|
|
|
* where 'types', 'inside', and 'outside' are comma-separated lists
|
|
|
|
|
* of tile types, 'resistance' is an integer giving the resistance
|
|
|
|
|
* per square in milli-ohms, and 'capacitance' is an integer giving
|
|
|
|
|
* capacitance (per square lambda for areacap, or per lambda perimeter
|
|
|
|
|
* for perimcap) in attofarads.
|
|
|
|
|
*
|
|
|
|
|
* The perimeter (sidewall) capacitance depends both on the types
|
|
|
|
|
* inside and outside the perimeter. For a given 'perimcap' line,
|
|
|
|
|
* any segment of perimeter with a type in 'inside' inside the
|
|
|
|
|
* perimeter and a type in 'outside' ontside the perimeter will
|
|
|
|
|
* have the indicated capacitance.
|
|
|
|
|
*
|
|
|
|
|
* Both area and perimeter capacitance computed from the information
|
|
|
|
|
* above apply between a given node and the substrate beneath it, as
|
|
|
|
|
* determined by extSubstrate[].
|
|
|
|
|
*
|
|
|
|
|
* Contact resistances are specified by:
|
|
|
|
|
*
|
|
|
|
|
* contact type minsize resistance
|
|
|
|
|
*
|
|
|
|
|
* where type is the type of contact tile, minsize is chosen so that contacts
|
|
|
|
|
* that are integer multiples of minsize get an additional contact cut for each
|
|
|
|
|
* increment of minsize, and resistance is in milliohms.
|
|
|
|
|
*
|
|
|
|
|
* Overlap coupling capacitance is specified by:
|
|
|
|
|
*
|
|
|
|
|
* overlap toptypes bottomtypes capacitance [shieldtypes]
|
|
|
|
|
*
|
|
|
|
|
* where 'toptypes' and 'bottomtypes' are comma-separated lists of tile types,
|
|
|
|
|
* and 'capacitance' is an integer giving capacitance in attofarads per
|
|
|
|
|
* square lambda of overlap. The sets 'toptypes' and 'bottomtypes' should
|
|
|
|
|
* be disjoint. Also, the union of the planes of 'toptypes' should be disjoint
|
|
|
|
|
* from the union of the planes of 'bottomtypes'. If 'shieldtypes' are
|
|
|
|
|
* present, they should also be a comma-separated list of types, on
|
|
|
|
|
* planes disjoint from those of either 'toptypes' or 'bottomtypes'.
|
|
|
|
|
*
|
|
|
|
|
* Whenever a tile of a type in 'toptypes' overlaps one of a type in
|
|
|
|
|
* 'bottomtypes', we deduct the capacitance to substrate of the 'toptypes'
|
|
|
|
|
* tile for the area of the overlap, and create an overlap capacitance
|
|
|
|
|
* between the two nodes based on 'capacitance'. When material in
|
|
|
|
|
* 'shieldtypes' appears over any of this overlap area, however, we
|
|
|
|
|
* only deduct the substrate capacitance; we don't create an overlap
|
|
|
|
|
* capacitor.
|
|
|
|
|
*
|
|
|
|
|
* Sidewall coupling capacitance is specified by:
|
|
|
|
|
*
|
|
|
|
|
* sidewall intypes outtypes neartypes fartypes capacitance
|
|
|
|
|
*
|
|
|
|
|
* where 'intypes', 'outtypes', 'neartypes', and 'fartypes' are all comma-
|
|
|
|
|
* separated lists of types, and 'capacitance' is an integer giving capacitance
|
|
|
|
|
* in attofarads. All of the tiles in all four lists should be on the same
|
|
|
|
|
* plane.
|
|
|
|
|
*
|
|
|
|
|
* Whenever an edge of the form i|j is seen, where 'i' is in intypes and
|
|
|
|
|
* 'j' is in outtypes, we search on the 'j' side for a distance of
|
|
|
|
|
* ExtCurStyle->exts_sideCoupleHalo for edges with 'neartypes' on the
|
|
|
|
|
* close side and 'fartypes' on the far side. We create a capacitance
|
|
|
|
|
* equal to the length of overlap, times capacitance, divided by the
|
|
|
|
|
* separation between the edges (poor approximation, but better than
|
|
|
|
|
* none).
|
|
|
|
|
*
|
|
|
|
|
* Sidewall overlap coupling capacitance is specified by:
|
|
|
|
|
*
|
|
|
|
|
* sideoverlap intypes outtypes ovtypes capacitance
|
|
|
|
|
*
|
|
|
|
|
* where 'intypes', 'outtypes', and 'ovtypes' are comma-separated lists
|
|
|
|
|
* of types, and 'capacitance' is an integer giving capacitance in attofarads
|
|
|
|
|
* per lambda. Both intypes and outtypes should be in the same plane, and
|
|
|
|
|
* ovtypes should be in a different plane from intypes and outtypes.
|
|
|
|
|
*
|
|
|
|
|
* The next kind of line describes transistors:
|
|
|
|
|
*
|
|
|
|
|
* fet types terminals min-#terminals names substrate gscap gccap
|
|
|
|
|
*
|
|
|
|
|
* where 'types' and 'terminals' are comma-separated lists of tile types.
|
|
|
|
|
* The meaning is that each type listed in 'types' is a transistor, whose
|
|
|
|
|
* source and drain connect to any of the types listed in 'terminals'.
|
|
|
|
|
* These transistors must have exactly min-#terminals terminals, in addition
|
|
|
|
|
* to the gate (whose connectivity is specified in the system-wide connectivity
|
|
|
|
|
* table in the "connect" section of the .tech file). Currently gscap and
|
|
|
|
|
* gccap are unused, but refer to the gate-source (or gate-drain) capacitance
|
|
|
|
|
* and the gate-channel capacitance in units of attofarads per lambda and
|
|
|
|
|
* attofarads per square lambda respectively.
|
|
|
|
|
*
|
|
|
|
|
* The resistances of transistors is specified by:
|
|
|
|
|
*
|
|
|
|
|
* fetresist type region ohms
|
|
|
|
|
*
|
|
|
|
|
* where type is a type of tile that is a fet, region is a string ("linear"
|
|
|
|
|
* is treated specially), and ohms is the resistance per square of the fet
|
|
|
|
|
* type while operating in "region". The values of fets in the "linear"
|
|
|
|
|
* region are stored in a separate table.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns TRUE normally, or FALSE if the line from the
|
|
|
|
|
* technology file is so malformed that Magic should abort.
|
|
|
|
|
* Currently, we always return TRUE.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Initializes the per-technology variables that appear at the
|
|
|
|
|
* beginning of this file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define MAXSD 6
|
|
|
|
|
|
|
|
|
|
/*ARGSUSED*/
|
|
|
|
|
bool
|
|
|
|
|
ExtTechLine(sectionName, argc, argv)
|
|
|
|
|
char *sectionName;
|
|
|
|
|
int argc;
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
int n, l, i, j, size, val, p1, p2, p3, nterm, iterm, class;
|
|
|
|
|
PlaneMask pshield, pov;
|
|
|
|
|
CapValue capVal, gscap, gccap;
|
2022-02-23 21:02:40 +01:00
|
|
|
ResValue resVal;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask types1, types2, termtypes[MAXSD];
|
2019-08-19 20:11:02 +02:00
|
|
|
TileTypeBitMask near, far, ov, shield, subsTypes, idTypes;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *subsName, *transName, *cp, *endptr, *paramName;
|
|
|
|
|
TileType s, t, r, o;
|
2024-10-10 21:13:24 +02:00
|
|
|
const keydesc *kp, *dv;
|
2017-04-25 14:41:48 +02:00
|
|
|
HashEntry *he;
|
|
|
|
|
EdgeCap *cnew;
|
|
|
|
|
ExtKeep *es, *newStyle;
|
|
|
|
|
ParamList *subcktParams, *newParam;
|
2019-08-19 20:11:02 +02:00
|
|
|
ExtDevice *devptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
int refcnt;
|
2022-05-28 16:33:21 +02:00
|
|
|
int offset;
|
2017-04-25 14:41:48 +02:00
|
|
|
double dhalo;
|
2022-05-28 16:33:21 +02:00
|
|
|
double doffset;
|
2017-04-25 14:41:48 +02:00
|
|
|
bool bad;
|
|
|
|
|
|
|
|
|
|
if (argc < 1)
|
|
|
|
|
{
|
|
|
|
|
TechError("Each line must begin with a keyword\n");
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-10 21:13:24 +02:00
|
|
|
n = LookupStruct(argv[0], (const LookupTable *) keyTable, sizeof keyTable[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (n < 0)
|
|
|
|
|
{
|
|
|
|
|
TechError("Illegal keyword. Legal keywords are:\n\t");
|
|
|
|
|
for (n = 0; keyTable[n].k_name; n++)
|
|
|
|
|
TxError(" %s", keyTable[n].k_name);
|
|
|
|
|
TxError("\n");
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kp = &keyTable[n];
|
|
|
|
|
if (argc < kp->k_minargs)
|
|
|
|
|
goto usage;
|
|
|
|
|
|
|
|
|
|
/* Handle maxargs for DEVICE type separately */
|
|
|
|
|
if ((argc > kp->k_maxargs) && (kp->k_key != DEVICE))
|
|
|
|
|
goto usage;
|
|
|
|
|
|
|
|
|
|
else if (argc >= 2) l = strlen(argv[1]);
|
|
|
|
|
|
|
|
|
|
/* If ExtCurStyle is NULL, this is a first pass, and we should */
|
|
|
|
|
/* immediately load this style as default. Otherwise, check if */
|
|
|
|
|
/* the style name is in the table of styles, and add it if it is */
|
|
|
|
|
/* not. */
|
|
|
|
|
|
|
|
|
|
if (kp->k_key == STYLE)
|
|
|
|
|
{
|
|
|
|
|
if (argc != 2)
|
|
|
|
|
if ((argc != 4) || (strncmp(argv[2], "variant", 7)))
|
|
|
|
|
goto usage;
|
|
|
|
|
|
|
|
|
|
for (newStyle = ExtAllStyles; newStyle != NULL;
|
|
|
|
|
newStyle = newStyle->exts_next)
|
|
|
|
|
{
|
|
|
|
|
if (!strncmp(newStyle->exts_name, argv[1], l))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (newStyle == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (argc == 2)
|
|
|
|
|
{
|
|
|
|
|
newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
|
|
|
|
|
newStyle->exts_next = NULL;
|
|
|
|
|
newStyle->exts_name = StrDup((char **) NULL, argv[1]);
|
|
|
|
|
|
|
|
|
|
if (ExtAllStyles == NULL)
|
|
|
|
|
ExtAllStyles = newStyle;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Append to end of style list */
|
|
|
|
|
for (es = ExtAllStyles; es->exts_next; es = es->exts_next);
|
|
|
|
|
es->exts_next = newStyle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else /* Handle style variants */
|
|
|
|
|
{
|
|
|
|
|
ExtKeep *saveStyle = NULL;
|
|
|
|
|
char *tptr, *cptr;
|
|
|
|
|
|
|
|
|
|
/* 4th argument is a comma-separated list of variants. */
|
|
|
|
|
/* In addition to the default name recorded above, */
|
|
|
|
|
/* record each of the variants. */
|
|
|
|
|
|
|
|
|
|
tptr = argv[3];
|
|
|
|
|
while (*tptr != '\0')
|
|
|
|
|
{
|
|
|
|
|
cptr = strchr(tptr, ',');
|
|
|
|
|
if (cptr != NULL) *cptr = '\0';
|
|
|
|
|
newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
|
|
|
|
|
newStyle->exts_next = NULL;
|
|
|
|
|
newStyle->exts_name = (char *)mallocMagic(l
|
|
|
|
|
+ strlen(tptr) + 1);
|
|
|
|
|
sprintf(newStyle->exts_name, "%s%s", argv[1], tptr);
|
|
|
|
|
|
|
|
|
|
/* Remember the first variant as the default */
|
|
|
|
|
if (saveStyle == NULL) saveStyle= newStyle;
|
|
|
|
|
|
|
|
|
|
/* Append to end of style list */
|
|
|
|
|
if (ExtAllStyles == NULL)
|
|
|
|
|
ExtAllStyles = newStyle;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (es = ExtAllStyles; es->exts_next; es = es->exts_next);
|
|
|
|
|
es->exts_next = newStyle;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (cptr == NULL)
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
tptr = cptr + 1;
|
|
|
|
|
}
|
|
|
|
|
newStyle = saveStyle;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Load style as default extraction style if this is the first */
|
|
|
|
|
/* style encountered. Otherwise, if we are changing styles, */
|
|
|
|
|
/* load this style only if the name matches that in ExtCurStyle.*/
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (ExtCurStyle == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
ExtCurStyle = extTechStyleNew();
|
|
|
|
|
ExtCurStyle->exts_name = newStyle->exts_name;
|
|
|
|
|
ExtCurStyle->exts_status = TECH_PENDING;
|
|
|
|
|
}
|
|
|
|
|
else if ((ExtCurStyle->exts_status == TECH_PENDING) ||
|
|
|
|
|
(ExtCurStyle->exts_status == TECH_SUSPENDED))
|
|
|
|
|
/* Finished loading; stop */
|
|
|
|
|
ExtCurStyle->exts_status = TECH_LOADED;
|
|
|
|
|
else if (ExtCurStyle->exts_status == TECH_NOT_LOADED)
|
|
|
|
|
{
|
|
|
|
|
if (ExtCurStyle->exts_name == NULL)
|
|
|
|
|
return (FALSE); /* Don't know what to load! */
|
|
|
|
|
else if (argc == 2)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(argv[1], ExtCurStyle->exts_name))
|
|
|
|
|
ExtCurStyle->exts_status = TECH_PENDING; /* load pending */
|
|
|
|
|
}
|
|
|
|
|
else if (argc == 4)
|
|
|
|
|
{
|
|
|
|
|
/* Verify that the style matches one variant */
|
|
|
|
|
char *tptr, *cptr;
|
|
|
|
|
if (!strncmp(ExtCurStyle->exts_name, argv[1], l))
|
|
|
|
|
{
|
|
|
|
|
tptr = argv[3];
|
|
|
|
|
while (*tptr != '\0')
|
|
|
|
|
{
|
|
|
|
|
cptr = strchr(tptr, ',');
|
|
|
|
|
if (cptr != NULL) *cptr = '\0';
|
|
|
|
|
if (!strcmp(ExtCurStyle->exts_name + l, tptr))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_status = TECH_PENDING;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (cptr == NULL)
|
|
|
|
|
return TRUE;
|
|
|
|
|
else
|
|
|
|
|
tptr = cptr + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only continue past this point if we are loading the extraction style */
|
|
|
|
|
if (ExtCurStyle == NULL) return FALSE;
|
|
|
|
|
if ((ExtCurStyle->exts_status != TECH_PENDING) &&
|
|
|
|
|
(ExtCurStyle->exts_status != TECH_SUSPENDED))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
/* Process "variant" lines next */
|
|
|
|
|
|
|
|
|
|
if (kp->k_key == VARIANT)
|
|
|
|
|
{
|
|
|
|
|
int l;
|
|
|
|
|
char *cptr, *tptr;
|
|
|
|
|
|
|
|
|
|
/* If our style variant is not one of the ones declared */
|
|
|
|
|
/* on the line, then we ignore all input until we */
|
|
|
|
|
/* either reach the end of the style, the end of the */
|
|
|
|
|
/* section, or another "variant" line. */
|
|
|
|
|
|
|
|
|
|
if (argc != 2) goto usage;
|
|
|
|
|
tptr = argv[1];
|
|
|
|
|
while (*tptr != '\0')
|
|
|
|
|
{
|
|
|
|
|
cptr = strchr(tptr, ',');
|
2020-05-23 23:13:14 +02:00
|
|
|
if (cptr != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
*cptr = '\0';
|
|
|
|
|
for (j = 1; isspace(*(cptr - j)); j++)
|
|
|
|
|
*(cptr - j) = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*tptr == '*') /* Wildcard for "all variants" */
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_status = TECH_PENDING;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
l = strlen(ExtCurStyle->exts_name) - strlen(tptr);
|
|
|
|
|
if (!strcmp(tptr, ExtCurStyle->exts_name + l))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_status = TECH_PENDING;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (cptr == NULL)
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
tptr = cptr + 1;
|
|
|
|
|
}
|
|
|
|
|
ExtCurStyle->exts_status = TECH_SUSPENDED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Anything below this line is not parsed if we're in TECH_SUSPENDED mode */
|
|
|
|
|
if (ExtCurStyle->exts_status != TECH_PENDING) return TRUE;
|
|
|
|
|
|
|
|
|
|
switch (kp->k_key)
|
|
|
|
|
{
|
|
|
|
|
case AREAC:
|
|
|
|
|
case CONTACT:
|
|
|
|
|
case FET:
|
|
|
|
|
case FETRESIST:
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
case DEVRESIST:
|
2017-04-25 14:41:48 +02:00
|
|
|
case HEIGHT:
|
2019-10-17 02:53:03 +02:00
|
|
|
case ANTENNA:
|
2019-10-18 20:12:52 +02:00
|
|
|
case TIEDOWN:
|
2017-04-25 14:41:48 +02:00
|
|
|
case OVERC:
|
|
|
|
|
case PERIMC:
|
|
|
|
|
case RESIST:
|
|
|
|
|
case SIDEWALL:
|
|
|
|
|
case SIDEOVERLAP:
|
|
|
|
|
case SUBSTRATE:
|
|
|
|
|
DBTechNoisyNameMask(argv[1], &types1);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types1);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEVICE:
|
|
|
|
|
DBTechNoisyNameMask(argv[3], &types1);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types1);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case PLANEORDER:
|
|
|
|
|
case NOPLANEORDER:
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (kp->k_key)
|
|
|
|
|
{
|
|
|
|
|
case AREAC:
|
|
|
|
|
capVal = aToCap(argv[2]);
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
2023-01-27 17:47:37 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_areaCap[t] = capVal;
|
2023-01-27 17:47:37 +01:00
|
|
|
ExtCurStyle->exts_overlapMult[t][0] = (float) capVal * FRINGE_MULT;
|
|
|
|
|
ExtCurStyle->exts_overlapMult[0][t] = (float) capVal * FRINGE_MULT;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case CONTACT:
|
|
|
|
|
/* Contact size, border, spacing deprecated (now taken from */
|
|
|
|
|
/* cifoutput "squares" generation parameters). */
|
|
|
|
|
if (argc != 3)
|
|
|
|
|
{
|
|
|
|
|
if (argc == 4)
|
|
|
|
|
TxPrintf("Contact size value ignored "
|
|
|
|
|
"(using GDS generation rules).\n");
|
|
|
|
|
else
|
|
|
|
|
TxPrintf("Contact size, spacing, and border values ignored "
|
|
|
|
|
"(using GDS generation rules).\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!StrIsInt(argv[argc - 1]))
|
|
|
|
|
{
|
|
|
|
|
TechError("Contact resistivity %s must be an integer value "
|
|
|
|
|
"(in milliohms/square).\n", argv[argc - 1]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-02-23 21:02:40 +01:00
|
|
|
resVal = aToRes(argv[argc - 1]);
|
2017-04-25 14:41:48 +02:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
2022-02-23 21:02:40 +01:00
|
|
|
ExtCurStyle->exts_viaResist[t] = resVal;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case CSCALE:
|
|
|
|
|
ExtCurStyle->exts_capScale = strtol(argv[1], &endptr, 10);
|
|
|
|
|
if (endptr == argv[1])
|
|
|
|
|
{
|
|
|
|
|
TechError("Cannot parse cap scale value \"%s\"\n", argv[1]);
|
|
|
|
|
ExtCurStyle->exts_capScale = 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case FET:
|
|
|
|
|
|
|
|
|
|
/* Original FET format, kept for backwards compatibility */
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[2], &termtypes[0]);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
nterm = atoi(argv[3]);
|
|
|
|
|
transName = argv[4];
|
|
|
|
|
subsName = argv[5];
|
|
|
|
|
|
|
|
|
|
// From magic version 8.1, subs name can be a nonfunctional
|
2020-05-23 23:13:14 +02:00
|
|
|
// throwaway (e.g., "error"), so don't throw a warning.
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
cp = strchr(subsName, '!');
|
|
|
|
|
if (cp == NULL || cp[1] != '\0')
|
|
|
|
|
{
|
|
|
|
|
if (strcasecmp(subsName, "error"))
|
|
|
|
|
{
|
|
|
|
|
TechError("Fet substrate node %s is not a global name\n",
|
|
|
|
|
subsName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subsTypes = DBZeroTypeBits;
|
|
|
|
|
if (sscanf(argv[6], "%lf", &capVal) != 1)
|
|
|
|
|
{
|
|
|
|
|
DBTechNoisyNameMask(argv[6], &subsTypes);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
2017-04-25 14:41:48 +02:00
|
|
|
gscap = aToCap(argv[7]);
|
|
|
|
|
gccap = (argc > 8) ? aToCap(argv[8]) : (CapValue) 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gscap = aToCap(argv[6]);
|
|
|
|
|
gccap = (argc > 7) ? aToCap(argv[7]) : (CapValue) 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1);
|
2017-04-25 14:41:48 +02:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr = (ExtDevice *)mallocMagic(sizeof(ExtDevice));
|
|
|
|
|
devptr->exts_deviceSDTypes = (TileTypeBitMask *)
|
2017-04-25 14:41:48 +02:00
|
|
|
mallocMagic(2 * sizeof(TileTypeBitMask));
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr->exts_deviceSDTypes[0] = termtypes[0];
|
|
|
|
|
devptr->exts_deviceSDTypes[1] = DBZeroTypeBits;
|
|
|
|
|
devptr->exts_deviceSDCount = nterm;
|
|
|
|
|
devptr->exts_deviceSDCap = gscap;
|
|
|
|
|
devptr->exts_deviceGateCap = gccap;
|
|
|
|
|
devptr->exts_deviceClass = DEV_FET;
|
|
|
|
|
devptr->exts_deviceName = StrDup((char **) NULL, transName);
|
|
|
|
|
devptr->exts_deviceSubstrateName =
|
2017-04-25 14:41:48 +02:00
|
|
|
StrDup((char **) NULL, subsName);
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr->exts_deviceSubstrateTypes = subsTypes;
|
|
|
|
|
devptr->exts_deviceIdentifierTypes = DBZeroTypeBits;
|
|
|
|
|
devptr->exts_deviceParams = (ParamList *) NULL;
|
|
|
|
|
devptr->exts_deviceResist.ht_table = (HashEntry **) NULL;
|
|
|
|
|
HashInit(&devptr->exts_deviceResist, 8, HT_STRINGKEYS);
|
|
|
|
|
|
|
|
|
|
TTMaskSetMask(ExtCurStyle->exts_deviceConn + t, &types1);
|
|
|
|
|
|
|
|
|
|
devptr->exts_next = ExtCurStyle->exts_device[t];
|
|
|
|
|
ExtCurStyle->exts_device[t] = devptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef ARIEL
|
|
|
|
|
{
|
|
|
|
|
int z;
|
|
|
|
|
|
|
|
|
|
for (z = TT_TECHDEPBASE; z < DBNumTypes; z++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&subsTypes, z))
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_subsTransistorTypes[z],
|
|
|
|
|
t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEFAULTAREACAP:
|
|
|
|
|
ExtTechSimpleAreaCap(argc, argv);
|
|
|
|
|
break;
|
|
|
|
|
case DEFAULTOVERLAP:
|
|
|
|
|
ExtTechSimpleOverlapCap(argv);
|
|
|
|
|
break;
|
|
|
|
|
case DEFAULTPERIMETER:
|
|
|
|
|
ExtTechSimplePerimCap(argc, argv);
|
|
|
|
|
break;
|
|
|
|
|
case DEFAULTSIDEOVERLAP:
|
|
|
|
|
ExtTechSimpleSideOverlapCap(argv);
|
|
|
|
|
break;
|
|
|
|
|
case DEFAULTSIDEWALL:
|
2022-05-28 16:33:21 +02:00
|
|
|
ExtTechSimpleSidewallCap(argc, argv);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEVICE:
|
|
|
|
|
|
|
|
|
|
/* Parse second argument for device type */
|
|
|
|
|
|
2024-10-10 21:13:24 +02:00
|
|
|
n = LookupStruct(argv[1], (const LookupTable *)devTable, sizeof devTable[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (n < 0)
|
|
|
|
|
{
|
|
|
|
|
TechError("Illegal device. Legal devices are:\n\t");
|
|
|
|
|
for (n = 0; devTable[n].k_name; n++)
|
|
|
|
|
TxError(" %s", devTable[n].k_name);
|
|
|
|
|
TxError("\n");
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dv = &devTable[n];
|
|
|
|
|
if ((argc - 1) < dv->k_minargs)
|
|
|
|
|
goto usage;
|
|
|
|
|
|
|
|
|
|
/* Parse parameters from the end of the argument list. */
|
|
|
|
|
/* Parameters may be provided for any device. */
|
|
|
|
|
|
|
|
|
|
/* Check final arguments for "x=y" statements showing what */
|
|
|
|
|
/* parameter names the device uses. */
|
|
|
|
|
|
|
|
|
|
subcktParams = NULL;
|
|
|
|
|
while ((paramName = strchr(argv[argc - 1], '=')) != NULL)
|
|
|
|
|
{
|
2023-06-22 02:44:38 +02:00
|
|
|
char *mult, *offset;
|
2023-10-25 02:29:04 +02:00
|
|
|
double dval;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2023-07-06 18:35:47 +02:00
|
|
|
/* Ignore ">=" and "<=", which are handled below */
|
|
|
|
|
if (paramName > argv[argc - 1])
|
|
|
|
|
if ((*(paramName - 1) == '>') || (*(paramName - 1) == '<'))
|
|
|
|
|
break;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
paramName++;
|
|
|
|
|
newParam = (ParamList *)mallocMagic(sizeof(ParamList));
|
|
|
|
|
newParam->pl_count = 0;
|
|
|
|
|
newParam->pl_param[0] = *argv[argc - 1];
|
|
|
|
|
newParam->pl_param[1] = '\0';
|
2023-07-06 18:35:47 +02:00
|
|
|
newParam->pl_maximum = -1;
|
|
|
|
|
newParam->pl_minimum = 0;
|
2023-10-25 02:29:04 +02:00
|
|
|
newParam->pl_offset = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (paramName - argv[argc - 1] == 3)
|
|
|
|
|
newParam->pl_param[1] = *(argv[argc - 1] + 1);
|
|
|
|
|
|
|
|
|
|
else if (paramName - argv[argc - 1] > 3)
|
|
|
|
|
TechError("Parameter name %s can be no more than"
|
|
|
|
|
"two characters.\n", argv[argc - 1]);
|
|
|
|
|
|
|
|
|
|
// Parameter syntax "<type>=<name>*<scale>" indicates
|
|
|
|
|
// that the subcircuit has internal scaling, and the
|
|
|
|
|
// extractor should multiply the parameter by this value
|
|
|
|
|
// before passing it to the subcircuit.
|
|
|
|
|
|
|
|
|
|
if ((mult = strchr(paramName, '*')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*mult = '\0';
|
|
|
|
|
mult++;
|
|
|
|
|
newParam->pl_scale = atof(mult);
|
|
|
|
|
}
|
|
|
|
|
else
|
2023-06-23 14:39:59 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
newParam->pl_scale = 1.0;
|
|
|
|
|
|
2023-06-23 14:39:59 +02:00
|
|
|
/* NOTE: If allowing both scale and offset, be sure
|
|
|
|
|
* to distinguish between +/- used for offsets and
|
|
|
|
|
* +/- used as sign.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if ((offset = strchr(paramName, '+')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*offset = '\0';
|
|
|
|
|
offset++;
|
2023-10-25 02:29:04 +02:00
|
|
|
dval = atof(offset);
|
|
|
|
|
newParam->pl_offset = (int)(0.5 + (dval * 1000));
|
2023-06-23 14:39:59 +02:00
|
|
|
}
|
|
|
|
|
else if ((offset = strchr(paramName, '-')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*offset = '\0';
|
|
|
|
|
offset++;
|
2023-10-25 02:29:04 +02:00
|
|
|
dval = -atof(offset);
|
|
|
|
|
newParam->pl_offset = (int)(0.5 + (dval * 1000));
|
2023-06-23 14:39:59 +02:00
|
|
|
}
|
|
|
|
|
else
|
2023-10-25 02:29:04 +02:00
|
|
|
newParam->pl_offset = 0;
|
2023-06-22 02:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
newParam->pl_name = StrDup((char **)NULL, paramName);
|
|
|
|
|
newParam->pl_next = subcktParams;
|
|
|
|
|
subcktParams = newParam;
|
|
|
|
|
argc--;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 18:35:47 +02:00
|
|
|
/* Check for parameter range limits in one of these forms: */
|
|
|
|
|
/* x>y, x<y, x>=y, x<=y. */
|
|
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
ParamList *chkParam;
|
|
|
|
|
char *limitstr;
|
|
|
|
|
char cond;
|
|
|
|
|
bool equal = FALSE;
|
|
|
|
|
double dval;
|
|
|
|
|
int ival;
|
|
|
|
|
|
|
|
|
|
limitstr = strchr(argv[argc - 1], '<');
|
|
|
|
|
if (limitstr == NULL)
|
|
|
|
|
limitstr = strchr(argv[argc - 1], '>');
|
|
|
|
|
if (limitstr == NULL) break;
|
|
|
|
|
|
|
|
|
|
cond = *limitstr;
|
|
|
|
|
*limitstr = '\0';
|
|
|
|
|
|
|
|
|
|
/* If the parameter exists, then modify its min/max values.
|
|
|
|
|
* If not, then create a parameter and fill in only the
|
|
|
|
|
* min/max values.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (limitstr - argv[argc - 1] > 3)
|
|
|
|
|
{
|
|
|
|
|
TechError("Parameter name %s can be no more than"
|
|
|
|
|
"two characters.\n", argv[argc - 1]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (chkParam = subcktParams; chkParam; chkParam =
|
|
|
|
|
chkParam->pl_next)
|
|
|
|
|
if ((chkParam->pl_param[0] == argv[argc - 1][0]) &&
|
|
|
|
|
(chkParam->pl_param[1] == argv[argc - 1][1]))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* If there is no defined parameter with the given name
|
|
|
|
|
* to be output, then create the parameter for checking
|
|
|
|
|
* limits only.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (chkParam == NULL)
|
|
|
|
|
{
|
|
|
|
|
newParam = (ParamList *)mallocMagic(sizeof(ParamList));
|
|
|
|
|
newParam->pl_count = 0;
|
|
|
|
|
newParam->pl_param[0] = argv[argc - 1][0];
|
|
|
|
|
newParam->pl_param[1] = argv[argc - 1][1];
|
|
|
|
|
newParam->pl_maximum = -1;
|
|
|
|
|
newParam->pl_minimum = 0;
|
|
|
|
|
newParam->pl_name = NULL;
|
|
|
|
|
newParam->pl_scale = 1.0;
|
2023-10-25 02:29:04 +02:00
|
|
|
newParam->pl_offset = 0;
|
2023-07-06 18:35:47 +02:00
|
|
|
|
|
|
|
|
newParam->pl_next = subcktParams;
|
|
|
|
|
subcktParams = newParam;
|
|
|
|
|
chkParam = newParam;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Change limit */
|
|
|
|
|
|
|
|
|
|
limitstr++;
|
|
|
|
|
if (*limitstr == '=')
|
|
|
|
|
{
|
|
|
|
|
equal = TRUE;
|
|
|
|
|
limitstr++;
|
|
|
|
|
}
|
2024-09-30 02:49:35 +02:00
|
|
|
if (sscanf(limitstr, "%lg", &dval) != 1)
|
2023-07-06 18:35:47 +02:00
|
|
|
{
|
|
|
|
|
TxError("Non-numeric limit \"%s\" for parameter \"%c%s\".\n",
|
|
|
|
|
limitstr, cond, argv[argc - 1]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert dval to internal (integer) units. Scale up by */
|
|
|
|
|
/* 1000 so the value can be converted if it's in microns */
|
|
|
|
|
/* without losing precision. */
|
|
|
|
|
ival = (int)(0.5 + (dval * 1000));
|
|
|
|
|
|
|
|
|
|
/* Make adjustment for greater than or less than */
|
|
|
|
|
if (cond == '>')
|
|
|
|
|
{
|
|
|
|
|
if (!equal) ival++;
|
|
|
|
|
chkParam->pl_minimum = ival;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!equal) ival--;
|
|
|
|
|
chkParam->pl_maximum = ival;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Move to next argument */
|
|
|
|
|
argc--;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
/* If the last entry before any parameters starts with '+', */
|
|
|
|
|
/* then use it to set the identity marker. Otherwise, the */
|
|
|
|
|
/* identity marker is NULL. */
|
|
|
|
|
|
|
|
|
|
idTypes = DBZeroTypeBits;
|
|
|
|
|
if (*argv[argc - 1] == '+')
|
|
|
|
|
{
|
|
|
|
|
if ((DBTechNameMask(argv[argc - 1] + 1, &idTypes)) == 0)
|
|
|
|
|
idTypes = DBZeroTypeBits;
|
|
|
|
|
argc--;
|
|
|
|
|
}
|
|
|
|
|
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
class = dv->k_key;
|
|
|
|
|
|
|
|
|
|
/* Note: This check has been removed. Parameters for non- */
|
|
|
|
|
/* subcircuit devices are allowed for support of CDL */
|
|
|
|
|
/* netlists, which uses arbitrary subcircuit-like */
|
|
|
|
|
/* parameters combined with a SPICE-like device prefix. */
|
|
|
|
|
#if 0
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Check the number of arguments after splitting out */
|
|
|
|
|
/* parameter entries. There is no limit on arguments in */
|
2025-08-07 17:54:49 +02:00
|
|
|
/* DEV_SUBCKT, DEV_MSUBCKT, and DEV_VERILOGA. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
switch (class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
case DEV_MSUBCKT:
|
2025-08-07 17:54:49 +02:00
|
|
|
case DEV_VERILOGA:
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* If parameters were saved but the */
|
|
|
|
|
/* argument list indicates a bad */
|
|
|
|
|
/* device entry, then free up the */
|
|
|
|
|
/* parameters. */
|
|
|
|
|
|
|
|
|
|
if ((argc - 1) > dv->k_maxargs)
|
|
|
|
|
{
|
|
|
|
|
while (subcktParams != NULL)
|
|
|
|
|
{
|
2023-07-06 18:35:47 +02:00
|
|
|
if (subcktParams->pl_name != NULL)
|
|
|
|
|
freeMagic(subcktParams->pl_name);
|
2017-04-25 14:41:48 +02:00
|
|
|
freeMagic(subcktParams);
|
|
|
|
|
subcktParams = subcktParams->pl_next;
|
|
|
|
|
}
|
|
|
|
|
goto usage;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
gscap = (CapValue) 0;
|
|
|
|
|
gccap = (CapValue) 0;
|
|
|
|
|
subsName = NULL;
|
|
|
|
|
subsTypes = DBZeroTypeBits;
|
|
|
|
|
transName = argv[2];
|
|
|
|
|
|
|
|
|
|
switch (dv->k_key)
|
|
|
|
|
{
|
|
|
|
|
case DEV_BJT:
|
|
|
|
|
DBTechNoisyNameMask(argv[4], &termtypes[0]); /* emitter */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[1] = DBZeroTypeBits;
|
|
|
|
|
DBTechNoisyNameMask(argv[5], &subsTypes); /* collector */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
2017-04-25 14:41:48 +02:00
|
|
|
nterm = 1; /* emitter is the only "terminal type" expected */
|
|
|
|
|
break;
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
if ((argc > 7) && (!StrIsNumeric(argv[7])))
|
|
|
|
|
{
|
|
|
|
|
/* Asymmetric device with different source and drain types */
|
|
|
|
|
|
|
|
|
|
DBTechNoisyNameMask(argv[4], &termtypes[0]); /* source */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[5], &termtypes[1]); /* drain */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[1]);
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskAndMask3(&termtypes[2], &termtypes[0], &termtypes[1]);
|
|
|
|
|
|
|
|
|
|
if (TTMaskEqual(&termtypes[0], &termtypes[1]))
|
|
|
|
|
termtypes[1] = DBZeroTypeBits; /* Make it symmetric */
|
|
|
|
|
else if (!TTMaskIsZero(&termtypes[2]))
|
|
|
|
|
{
|
2021-01-17 18:52:57 +01:00
|
|
|
class = DEV_ASYMMETRIC;
|
2017-04-25 14:41:48 +02:00
|
|
|
TechError("Device mosfet %s has overlapping drain"
|
|
|
|
|
" and source types!\n", transName);
|
|
|
|
|
/* Should this device be disabled? */
|
|
|
|
|
}
|
2021-01-17 18:52:57 +01:00
|
|
|
else
|
|
|
|
|
class = DEV_ASYMMETRIC;
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[2] = DBZeroTypeBits;
|
|
|
|
|
if (strcmp(argv[6], "None"))
|
|
|
|
|
DBTechNoisyNameMask(argv[6], &subsTypes); /* substrate */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
2017-04-25 14:41:48 +02:00
|
|
|
subsName = argv[7];
|
|
|
|
|
if (argc > 8) gscap = aToCap(argv[8]);
|
|
|
|
|
if (argc > 9) gccap = aToCap(argv[9]);
|
|
|
|
|
nterm = 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Normal symmetric device with swappable source/drain */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[4], &termtypes[0]); /* source/drain */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[1] = DBZeroTypeBits;
|
|
|
|
|
if (strcmp(argv[5], "None"))
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (argc > 6) subsName = argv[6];
|
|
|
|
|
if (argc > 7) gscap = aToCap(argv[7]);
|
|
|
|
|
if (argc > 8) gccap = aToCap(argv[8]);
|
|
|
|
|
/* nterm = 1; */ /* Symmetric devices can be MOScaps */
|
|
|
|
|
nterm = 2;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
DBTechNoisyNameMask(argv[4], &termtypes[0]); /* negative types */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[1] = DBZeroTypeBits;
|
|
|
|
|
nterm = 1;
|
|
|
|
|
if ((argc > 4) && strcmp(argv[4], "None"))
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[4], &subsTypes); /* substrate */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
subsTypes = DBZeroTypeBits;
|
|
|
|
|
if (argc > 5) subsName = argv[5];
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
DBTechNoisyNameMask(argv[4], &termtypes[0]); /* terminals */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[1] = DBZeroTypeBits;
|
|
|
|
|
nterm = 2;
|
|
|
|
|
if ((argc > 5) && strcmp(argv[5], "None"))
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
subsTypes = DBZeroTypeBits;
|
|
|
|
|
if (argc > 6) subsName = argv[6];
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
DBTechNoisyNameMask(argv[4], &termtypes[0]); /* bottom */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[1] = DBZeroTypeBits;
|
|
|
|
|
|
|
|
|
|
if (argc > 5)
|
|
|
|
|
gccap = aToCap(argv[argc - 1]); /* area cap */
|
|
|
|
|
if ((argc > 6) && StrIsNumeric(argv[argc - 2]))
|
|
|
|
|
{
|
|
|
|
|
gscap = aToCap(argv[argc - 2]); /* perimeter cap */
|
|
|
|
|
argc--;
|
|
|
|
|
}
|
|
|
|
|
nterm = 1;
|
|
|
|
|
|
|
|
|
|
if ((argc > 6) && strcmp(argv[5], "None"))
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
subsTypes = DBZeroTypeBits;
|
|
|
|
|
if (argc > 7) subsName = argv[6];
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
case DEV_MSUBCKT:
|
2025-08-07 17:54:49 +02:00
|
|
|
case DEV_VERILOGA:
|
2017-08-02 04:14:42 +02:00
|
|
|
// Determine if [substrate, name] optional arguments
|
|
|
|
|
// are present by checking if the last argument
|
|
|
|
|
// parses as a layer list.
|
|
|
|
|
|
|
|
|
|
if (DBTechNameMask(argv[argc - 1], &termtypes[0]) <= 0)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(argv[argc - 2], "None"))
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
DBTechNoisyNameMask(argv[argc - 2], &subsTypes);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
else
|
|
|
|
|
subsTypes = DBZeroTypeBits;
|
|
|
|
|
subsName = argv[argc - 1];
|
|
|
|
|
argc -= 2;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (StrIsInt(argv[4]))
|
|
|
|
|
{
|
|
|
|
|
nterm = atoi(argv[4]);
|
2017-08-02 04:14:42 +02:00
|
|
|
iterm = 5;
|
|
|
|
|
if (nterm > argc - 5)
|
|
|
|
|
{
|
|
|
|
|
TechError("Not enough terminals for subcircuit, "
|
|
|
|
|
"%d were required, %d found.\n",
|
|
|
|
|
nterm, argc - 5);
|
|
|
|
|
nterm = argc - 5;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
nterm = argc - 4;
|
2017-04-25 14:41:48 +02:00
|
|
|
iterm = 4;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* terminals */
|
|
|
|
|
for (i = iterm; i < iterm + nterm; i++)
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2020-09-09 18:18:09 +02:00
|
|
|
DBTechNoisyNameMask(argv[i], &termtypes[i - iterm]);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[i - iterm]);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[nterm] = DBZeroTypeBits;
|
|
|
|
|
|
|
|
|
|
if (nterm == 0) i++;
|
|
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
// Type MSUBCKT: If source and drain are symmetric (both
|
|
|
|
|
// have the same types), then they must both be declared,
|
|
|
|
|
// but only one is used (same policy as "device mosfet").
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2017-08-02 04:14:42 +02:00
|
|
|
if ((nterm == 2) && TTMaskEqual(&termtypes[nterm - 1],
|
|
|
|
|
&termtypes[nterm - 2]))
|
|
|
|
|
termtypes[nterm - 1] = DBZeroTypeBits;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
case DEV_CSUBCKT:
|
2018-10-31 02:56:05 +01:00
|
|
|
nterm = (dv->k_key == DEV_RSUBCKT) ? 2 : 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[4], &termtypes[0]); /* terminals */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &termtypes[0]);
|
2017-04-25 14:41:48 +02:00
|
|
|
termtypes[1] = DBZeroTypeBits;
|
|
|
|
|
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
if ((argc > 5) && strcmp(argv[5], "None") &&
|
|
|
|
|
(strchr(argv[5], '=') == NULL))
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[5], &subsTypes); /* substrate */
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &subsTypes);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
subsTypes = DBZeroTypeBits;
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
if ((argc > 6) && (strchr(argv[6], '=') == NULL))
|
|
|
|
|
subsName = argv[6];
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr = (ExtDevice *)mallocMagic(sizeof(ExtDevice));
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
for (i = 0; !TTMaskIsZero(&termtypes[i]); i++);
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr->exts_deviceSDTypes = (TileTypeBitMask *)
|
2017-04-25 14:41:48 +02:00
|
|
|
mallocMagic((i + 1) * sizeof(TileTypeBitMask));
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (i = 0; !TTMaskIsZero(&termtypes[i]); i++)
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr->exts_deviceSDTypes[i] = termtypes[i];
|
|
|
|
|
devptr->exts_deviceSDTypes[i] = DBZeroTypeBits;
|
|
|
|
|
|
|
|
|
|
devptr->exts_deviceSDCount = nterm;
|
|
|
|
|
devptr->exts_deviceSDCap = gscap;
|
|
|
|
|
devptr->exts_deviceGateCap = gccap;
|
|
|
|
|
devptr->exts_deviceClass = class;
|
|
|
|
|
devptr->exts_deviceName = StrDup((char **) NULL, transName);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (subsName != NULL)
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr->exts_deviceSubstrateName =
|
2017-04-25 14:41:48 +02:00
|
|
|
StrDup((char **) NULL, subsName);
|
2019-10-16 17:38:31 +02:00
|
|
|
else
|
|
|
|
|
devptr->exts_deviceSubstrateName = (char *)NULL;
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr->exts_deviceSubstrateTypes = subsTypes;
|
|
|
|
|
devptr->exts_deviceIdentifierTypes = idTypes;
|
|
|
|
|
devptr->exts_deviceParams = (ParamList *) NULL;
|
|
|
|
|
if (subcktParams != NULL)
|
|
|
|
|
{
|
|
|
|
|
devptr->exts_deviceParams = subcktParams;
|
|
|
|
|
subcktParams->pl_count++;
|
|
|
|
|
}
|
|
|
|
|
devptr->exts_deviceResist.ht_table = (HashEntry **) NULL;
|
|
|
|
|
HashInit(&devptr->exts_deviceResist, 8, HT_STRINGKEYS);
|
|
|
|
|
|
|
|
|
|
devptr->exts_next = ExtCurStyle->exts_device[t];
|
|
|
|
|
ExtCurStyle->exts_device[t] = devptr;
|
|
|
|
|
|
|
|
|
|
TTMaskSetMask(ExtCurStyle->exts_deviceConn + t, &types1);
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef ARIEL
|
|
|
|
|
{
|
|
|
|
|
int z;
|
|
|
|
|
|
|
|
|
|
for (z = TT_TECHDEPBASE; z < DBNumTypes; z++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&subsTypes, z))
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->
|
|
|
|
|
exts_subsTransistorTypes[z], t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
case DEVRESIST:
|
2017-04-25 14:41:48 +02:00
|
|
|
case FETRESIST:
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
if (!StrIsNumeric(argv[3]))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
TechError("Device resistivity %s must be numeric\n", argv[3]);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2022-02-23 21:02:40 +01:00
|
|
|
resVal = aToRes(argv[3]);
|
2017-04-25 14:41:48 +02:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
2019-08-19 20:11:02 +02:00
|
|
|
{
|
|
|
|
|
ExtDevice *devptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next)
|
|
|
|
|
{
|
|
|
|
|
he = HashFind(&devptr->exts_deviceResist, argv[2]);
|
2022-02-23 21:02:40 +01:00
|
|
|
HashSetValue(he, (spointertype)resVal);
|
2019-08-19 20:11:02 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-08-19 20:11:02 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case HEIGHT: {
|
|
|
|
|
float height, thick;
|
|
|
|
|
|
|
|
|
|
if (!StrIsNumeric(argv[2]))
|
|
|
|
|
{
|
|
|
|
|
TechError("Layer height %s must be numeric\n", argv[2]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!StrIsNumeric(argv[3]))
|
|
|
|
|
{
|
|
|
|
|
TechError("Layer thickness %s must be numeric\n", argv[3]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
height = (float)strtod(argv[2], NULL);
|
|
|
|
|
thick = (float)strtod(argv[3], NULL);
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_height[t] = height;
|
|
|
|
|
ExtCurStyle->exts_thick[t] = thick;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
2019-10-17 02:53:03 +02:00
|
|
|
case ANTENNA: {
|
|
|
|
|
float antennaratio;
|
2019-11-27 16:38:47 +01:00
|
|
|
char areaType;
|
|
|
|
|
bool hasModel = FALSE;
|
|
|
|
|
int argidx = 2;
|
2019-10-17 02:53:03 +02:00
|
|
|
|
|
|
|
|
if (!StrIsNumeric(argv[2]))
|
|
|
|
|
{
|
2019-11-27 16:38:47 +01:00
|
|
|
if (!strcmp(argv[2], "surface") || !strcmp(argv[2], "area"))
|
|
|
|
|
{
|
|
|
|
|
areaType = ANTENNAMODEL_SURFACE;
|
|
|
|
|
hasModel = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(argv[2], "sidewall") || !strcmp(argv[2], "perimeter"))
|
|
|
|
|
{
|
|
|
|
|
areaType = ANTENNAMODEL_SIDEWALL;
|
|
|
|
|
hasModel = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TechError("Error in layer antenna calculation type \"%s\"; "
|
|
|
|
|
" must be \"surface\" or \"sidewall\"\n", argv[2]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (hasModel == FALSE)
|
|
|
|
|
{
|
|
|
|
|
if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_SURFACE)
|
|
|
|
|
areaType = ANTENNAMODEL_SURFACE;
|
|
|
|
|
else if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_SIDEWALL)
|
|
|
|
|
areaType = ANTENNAMODEL_SIDEWALL;
|
|
|
|
|
else
|
|
|
|
|
TechError("No antenna calculation type given for layer(s) %s "
|
|
|
|
|
" and no default calculation type found.\n", argv[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
ExtCurStyle->exts_antennaRatio[t].areaType = areaType;
|
|
|
|
|
|
|
|
|
|
if (hasModel == TRUE) argidx = 3;
|
|
|
|
|
|
|
|
|
|
if (!StrIsNumeric(argv[argidx]))
|
|
|
|
|
{
|
|
|
|
|
TechError("Gate layer antenna ratio %s must be numeric\n", argv[argidx]);
|
2019-10-17 02:53:03 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2019-11-27 16:38:47 +01:00
|
|
|
antennaratio = (float)strtod(argv[argidx], NULL);
|
|
|
|
|
|
2019-10-17 02:53:03 +02:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
2019-10-21 04:12:02 +02:00
|
|
|
ExtCurStyle->exts_antennaRatio[t].ratioGate = antennaratio;
|
|
|
|
|
|
2019-11-27 16:38:47 +01:00
|
|
|
argidx++;
|
|
|
|
|
if (!StrIsNumeric(argv[argidx]))
|
2019-10-21 04:12:02 +02:00
|
|
|
{
|
2019-11-27 16:38:47 +01:00
|
|
|
if (!strcasecmp(argv[argidx], "none"))
|
2019-10-21 04:12:02 +02:00
|
|
|
antennaratio = INFINITY;
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-11-27 16:38:47 +01:00
|
|
|
TechError("Diff layer antenna ratio %s must be numeric\n",
|
|
|
|
|
argv[argidx]);
|
2019-10-21 04:12:02 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-11-27 16:38:47 +01:00
|
|
|
antennaratio = (float)strtod(argv[argidx], NULL);
|
|
|
|
|
|
2019-10-21 04:12:02 +02:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
2019-11-27 16:38:47 +01:00
|
|
|
ExtCurStyle->exts_antennaRatio[t].ratioDiffB = antennaratio;
|
|
|
|
|
|
|
|
|
|
argidx++;
|
|
|
|
|
if (argidx < argc)
|
|
|
|
|
{
|
|
|
|
|
if (!StrIsNumeric(argv[argidx]))
|
2019-10-21 04:12:02 +02:00
|
|
|
{
|
2019-11-27 16:38:47 +01:00
|
|
|
TechError("Diff layer antenna ratio %s must be numeric\n",
|
|
|
|
|
argv[argidx]);
|
|
|
|
|
break;
|
2019-10-17 02:53:03 +02:00
|
|
|
}
|
2019-11-27 16:38:47 +01:00
|
|
|
antennaratio = (float)strtod(argv[argidx], NULL);
|
2019-10-17 02:53:03 +02:00
|
|
|
}
|
2019-11-27 16:38:47 +01:00
|
|
|
else
|
|
|
|
|
antennaratio = 0;
|
|
|
|
|
|
2022-11-06 17:50:05 +01:00
|
|
|
/* NOTE: antennaratio is multiplied by diffusion area and so has
|
|
|
|
|
* units of (1/area^2) and so it should be scaled with other
|
|
|
|
|
* dimension-scaled units.
|
|
|
|
|
*/
|
2019-11-27 16:38:47 +01:00
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
ExtCurStyle->exts_antennaRatio[t].ratioDiffA = antennaratio;
|
|
|
|
|
|
2019-10-17 02:53:03 +02:00
|
|
|
break;
|
2019-11-27 16:38:47 +01:00
|
|
|
}
|
2019-10-21 04:12:02 +02:00
|
|
|
case MODEL:
|
|
|
|
|
if (!strcmp(argv[1], "partial"))
|
|
|
|
|
ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_PARTIAL;
|
|
|
|
|
else if (!strcmp(argv[1], "cumulative"))
|
|
|
|
|
ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_CUMULATIVE;
|
|
|
|
|
else
|
|
|
|
|
TxError("Unknown antenna model \"%s\": Use \"partial\" or "
|
2024-09-30 06:31:49 +02:00
|
|
|
"\"cumulative\"", argv[1]);
|
2019-10-21 04:12:02 +02:00
|
|
|
|
2019-11-27 16:38:47 +01:00
|
|
|
if (argc > 2)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(argv[2], "surface") || !strcmp(argv[2], "area"))
|
|
|
|
|
ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_SURFACE;
|
|
|
|
|
else if (!strcmp(argv[2], "sidewall") || !strcmp(argv[2], "perimeter"))
|
|
|
|
|
ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_SIDEWALL;
|
|
|
|
|
else
|
|
|
|
|
TxError("Unknown antenna model \"%s\": Use \"surface\" or "
|
2024-09-30 06:32:02 +02:00
|
|
|
"\"sidewall\"", argv[2]);
|
2019-11-27 16:38:47 +01:00
|
|
|
}
|
2019-10-21 04:12:02 +02:00
|
|
|
break;
|
|
|
|
|
|
2019-10-18 20:12:52 +02:00
|
|
|
case TIEDOWN:
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_antennaTieTypes, &types1);
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case UNITS:
|
|
|
|
|
if (!strcmp(argv[1], "microns"))
|
|
|
|
|
doConvert = TRUE;
|
|
|
|
|
else if (!strcmp(argv[1], "um"))
|
|
|
|
|
doConvert = TRUE;
|
|
|
|
|
else if (strcmp(argv[1], "lambda"))
|
|
|
|
|
TechError("Units must be microns or lambda. Using the "
|
|
|
|
|
"default value (lambda).\n");
|
|
|
|
|
break;
|
|
|
|
|
case LAMBDA:
|
|
|
|
|
ExtCurStyle->exts_unitsPerLambda = (float)atof(argv[1]);
|
|
|
|
|
break;
|
|
|
|
|
case OVERC:
|
|
|
|
|
DBTechNoisyNameMask(argv[2], &types2);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types2);
|
2017-04-25 14:41:48 +02:00
|
|
|
capVal = aToCap(argv[3]);
|
|
|
|
|
bad = FALSE;
|
|
|
|
|
shield = DBZeroTypeBits;
|
|
|
|
|
if (argc > 4)
|
2022-01-14 23:30:05 +01:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[4], &shield);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &shield);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types1, s)) continue;
|
|
|
|
|
|
|
|
|
|
/* Contact overlap caps are determined from residues */
|
|
|
|
|
if (DBIsContact(s)) continue;
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types2, t)) continue;
|
|
|
|
|
|
|
|
|
|
/* Contact overlap caps are determined from residues */
|
|
|
|
|
if (DBIsContact(t)) continue;
|
|
|
|
|
|
|
|
|
|
if (s == t)
|
|
|
|
|
{
|
|
|
|
|
TechError("Can't have overlap capacitance between"
|
|
|
|
|
" tiles of the same type (%s)\n",
|
|
|
|
|
DBTypeLongName(s));
|
|
|
|
|
bad = TRUE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
p1 = DBPlane(s), p2 = DBPlane(t);
|
|
|
|
|
if (p1 == p2)
|
|
|
|
|
{
|
|
|
|
|
TechError("Can't have overlap capacitance between"
|
|
|
|
|
" tiles on the same plane (%s, %s)\n",
|
|
|
|
|
DBTypeLongName(s), DBTypeLongName(t));
|
|
|
|
|
bad = TRUE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0)
|
|
|
|
|
{
|
|
|
|
|
TechError("Only one of \"overlap %s %s\" or"
|
|
|
|
|
" \"overlap %s %s\" allowed\n",
|
|
|
|
|
DBTypeLongName(s), DBTypeLongName(t),
|
|
|
|
|
DBTypeLongName(t), DBTypeLongName(s));
|
|
|
|
|
bad = TRUE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ExtCurStyle->exts_overlapCap[s][t] = capVal;
|
2023-01-27 17:47:37 +01:00
|
|
|
ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT;
|
|
|
|
|
ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT;
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(p1);
|
|
|
|
|
ExtCurStyle->exts_overlapOtherPlanes[s]
|
|
|
|
|
|= PlaneNumToMaskBit(p2);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_overlapTypes[p1], s);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t);
|
|
|
|
|
if (argc == 4) continue;
|
|
|
|
|
|
|
|
|
|
/* Shielding */
|
|
|
|
|
pshield = 0;
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&shield, r))
|
|
|
|
|
{
|
|
|
|
|
/* Shielding types are determined from residues */
|
|
|
|
|
if (DBIsContact(r)) continue;
|
|
|
|
|
|
|
|
|
|
p3 = DBPlane(r);
|
|
|
|
|
if (p3 == p1 || p3 == p2)
|
|
|
|
|
{
|
|
|
|
|
TechError("Shielding type (%s) must be on a"
|
|
|
|
|
" different plane from shielded types.\n",
|
|
|
|
|
DBTypeLongName(r));
|
|
|
|
|
bad = TRUE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
pshield |= PlaneNumToMaskBit(p3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield;
|
|
|
|
|
ExtCurStyle->exts_overlapShieldTypes[s][t] = shield;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bad)
|
|
|
|
|
return (TRUE);
|
|
|
|
|
break;
|
|
|
|
|
case SIDEOVERLAP:
|
|
|
|
|
bad = FALSE;
|
|
|
|
|
DBTechNoisyNameMask(argv[2], &types2);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types2);
|
2017-04-25 14:41:48 +02:00
|
|
|
pov = DBTechNoisyNameMask(argv[3], &ov);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &ov);
|
2017-04-25 14:41:48 +02:00
|
|
|
capVal = aToCap(argv[4]);
|
|
|
|
|
shield = DBZeroTypeBits;
|
2022-01-14 23:30:05 +01:00
|
|
|
if (argc == 6)
|
|
|
|
|
{
|
|
|
|
|
DBTechNoisyNameMask(argv[5], &shield);
|
|
|
|
|
TTMaskSetMask(allExtractTypes, &shield);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
if (TTMaskHasType(&types1, TT_SPACE))
|
|
|
|
|
TechError("Can't have space on inside of edge [ignored]\n");
|
|
|
|
|
/* It's ok to have the overlap be to space as long as a plane is */
|
|
|
|
|
/* specified. */
|
|
|
|
|
if (TTMaskHasType(&ov, TT_SPACE))
|
|
|
|
|
{
|
|
|
|
|
if ((cp = strchr(argv[3],'/')) == NULL)
|
|
|
|
|
{
|
|
|
|
|
TechError("Must specify plane for sideoverlap to space\n");
|
|
|
|
|
}
|
|
|
|
|
cp++;
|
|
|
|
|
p3 = (spointertype) dbTechNameLookup(cp, &dbPlaneNameLists);
|
|
|
|
|
if (p3 < 0)
|
|
|
|
|
TechError("Unknown overlap plane %s\n",argv[3]);
|
|
|
|
|
else
|
|
|
|
|
pov = PlaneNumToMaskBit(p3);
|
|
|
|
|
}
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types1, s))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Side overlap computed from residues */
|
|
|
|
|
if (DBIsContact(s)) continue;
|
|
|
|
|
|
|
|
|
|
p1 = DBPlane(s);
|
|
|
|
|
if (PlaneMaskHasPlane(pov, p1))
|
|
|
|
|
goto diffplane;
|
|
|
|
|
ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(p1);
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_sideTypes[p1], s);
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
|
|
|
|
|
for (t = 0; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types2, t))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Side overlap computed from residues */
|
|
|
|
|
if (DBIsContact(t)) continue;
|
|
|
|
|
|
|
|
|
|
p2 = DBPlane(t);
|
|
|
|
|
if (t != TT_SPACE && PlaneMaskHasPlane(pov, p2))
|
|
|
|
|
goto diffplane;
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &ov);
|
|
|
|
|
ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |= pov;
|
|
|
|
|
cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
|
|
|
|
|
cnew->ec_cap = capVal;
|
2022-05-28 16:33:21 +02:00
|
|
|
cnew->ec_offset = 0; /* No offsets on overlap caps */
|
2017-04-25 14:41:48 +02:00
|
|
|
cnew->ec_far = shield; /* Really types that shield */
|
|
|
|
|
cnew->ec_near = ov; /* Really types we create cap with */
|
|
|
|
|
cnew->ec_pmask = pov;
|
|
|
|
|
cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t];
|
|
|
|
|
ExtCurStyle->exts_sideOverlapCap[s][t] = cnew;
|
|
|
|
|
|
|
|
|
|
/* Shielding */
|
|
|
|
|
pshield = 0;
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&shield, r))
|
|
|
|
|
{
|
|
|
|
|
/* Side overlap shielding computed from residues */
|
|
|
|
|
if (DBIsContact(r)) continue;
|
|
|
|
|
|
|
|
|
|
p3 = DBPlane(r);
|
|
|
|
|
if (p3 == p1 || p3 == p2)
|
|
|
|
|
{
|
|
|
|
|
TechError("Shielding type (%s) must be on"
|
|
|
|
|
" a different plane from shielded types.\n",
|
|
|
|
|
DBTypeLongName(r));
|
|
|
|
|
bad = TRUE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
pshield |= PlaneNumToMaskBit(p3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (o = TT_TECHDEPBASE; o < DBNumTypes; o++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&ov, o))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_sideOverlapShieldPlanes[s][o] |= pshield;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bad)
|
|
|
|
|
return (TRUE);
|
|
|
|
|
break;
|
|
|
|
|
case SIDEWALL:
|
|
|
|
|
DBTechNoisyNameMask(argv[2], &types2);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types2);
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[3], &near);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &near);
|
2017-04-25 14:41:48 +02:00
|
|
|
DBTechNoisyNameMask(argv[4], &far);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &far);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (TTMaskHasType(&types1, TT_SPACE))
|
|
|
|
|
TechError("Can't have space on inside of edge [ignored]\n");
|
|
|
|
|
capVal = aToCap(argv[5]);
|
2022-05-28 16:33:21 +02:00
|
|
|
if (argc == 7)
|
|
|
|
|
{
|
|
|
|
|
sscanf(argv[6], "%lg", &doffset);
|
|
|
|
|
offset = (int)(0.5 + doffset * 1000.0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
offset = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types1, s))
|
|
|
|
|
continue;
|
|
|
|
|
ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(DBPlane(s));
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_sideTypes[DBPlane(s)], s);
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
|
|
|
|
|
for (t = 0; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&types2, t))
|
|
|
|
|
continue;
|
|
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_sideCoupleOtherEdges[s][t], &far);
|
|
|
|
|
cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
|
|
|
|
|
cnew->ec_cap = capVal;
|
2022-05-28 16:33:21 +02:00
|
|
|
cnew->ec_offset = offset;
|
2017-04-25 14:41:48 +02:00
|
|
|
cnew->ec_near = near;
|
|
|
|
|
cnew->ec_far = far;
|
|
|
|
|
cnew->ec_next = ExtCurStyle->exts_sideCoupleCap[s][t];
|
|
|
|
|
cnew->ec_pmask = 0;
|
|
|
|
|
ExtCurStyle->exts_sideCoupleCap[s][t] = cnew;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SIDEHALO:
|
|
|
|
|
/* Allow floating-point and increase by factor of 1000 */
|
|
|
|
|
/* to accommodate "units microns". */
|
|
|
|
|
|
|
|
|
|
/* Warning: Due to some gcc bug with an i686 FPU, using a */
|
|
|
|
|
/* result from atof() with a static value like 1000 */
|
|
|
|
|
/* produces a NaN result! sscanf() seems to be safe. . . */
|
|
|
|
|
|
|
|
|
|
sscanf(argv[1], "%lg", &dhalo);
|
|
|
|
|
dhalo *= (double)1000.0;
|
|
|
|
|
ExtCurStyle->exts_sideCoupleHalo = (int)dhalo;
|
|
|
|
|
break;
|
2022-03-17 22:35:41 +01:00
|
|
|
case FRINGESHIELDHALO:
|
2023-01-27 17:47:37 +01:00
|
|
|
/* This is deprecated. . . Ignore */
|
2022-03-17 22:35:41 +01:00
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case PERIMC:
|
|
|
|
|
DBTechNoisyNameMask(argv[2], &types2);
|
2022-01-14 23:30:05 +01:00
|
|
|
TTMaskSetMask(allExtractTypes, &types2);
|
2017-04-25 14:41:48 +02:00
|
|
|
capVal = aToCap(argv[3]);
|
|
|
|
|
if (capVal == (CapValue) 0)
|
|
|
|
|
break;
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
for (t = 0; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, s) && TTMaskHasType(&types2, t))
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_perimCap[s][t] = capVal;
|
|
|
|
|
TTMaskSetType(&ExtCurStyle->exts_perimCapMask[s], t);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case RESIST: {
|
|
|
|
|
float chop = 1.0;
|
|
|
|
|
|
|
|
|
|
if (!StrIsInt(argv[2]))
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(argv[2], "None"))
|
|
|
|
|
{
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
TTMaskClearType(&ExtCurStyle->exts_activeTypes, t);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TxError("Resist argument must be integer or \"None\".\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-02-23 21:02:40 +01:00
|
|
|
resVal = aToRes(argv[2]);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (argc == 4)
|
|
|
|
|
chop = atof(argv[3]);
|
|
|
|
|
class = ExtCurStyle->exts_numResistClasses++;
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
if (TTMaskHasType(&types1, t))
|
|
|
|
|
{
|
2022-02-23 21:02:40 +01:00
|
|
|
ExtCurStyle->exts_sheetResist[t] = resVal;
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_cornerChop[t] = chop;
|
|
|
|
|
ExtCurStyle->exts_typeToResistClass[t] = class;
|
|
|
|
|
}
|
2022-02-23 21:02:40 +01:00
|
|
|
ExtCurStyle->exts_resistByResistClass[class] = resVal;
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_typesByResistClass[class] = types1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case RSCALE:
|
|
|
|
|
ExtCurStyle->exts_resistScale = atoi(argv[1]);
|
|
|
|
|
break;
|
|
|
|
|
case STEP:
|
|
|
|
|
val = (int)atof(argv[1]);
|
|
|
|
|
if (val <= 0)
|
|
|
|
|
{
|
|
|
|
|
TechError("Hierarchical interaction step size must be > 0\n");
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
ExtCurStyle->exts_stepSize = val;
|
|
|
|
|
break;
|
|
|
|
|
case SUBSTRATE:
|
2019-09-19 02:48:33 +02:00
|
|
|
/* If the last entry starts with '-', then use it to set */
|
|
|
|
|
/* the shield types. Otherwise, the shield types mask is */
|
|
|
|
|
/* NULL. */
|
|
|
|
|
|
|
|
|
|
idTypes = DBZeroTypeBits;
|
|
|
|
|
if (*argv[argc - 1] == '-')
|
|
|
|
|
{
|
|
|
|
|
if ((DBTechNameMask(argv[argc - 1] + 1, &idTypes)) == 0)
|
|
|
|
|
idTypes = DBZeroTypeBits;
|
2022-01-14 23:42:21 +01:00
|
|
|
else
|
|
|
|
|
TTMaskSetMask(allExtractTypes, &idTypes);
|
2019-09-19 02:48:33 +02:00
|
|
|
argc--;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskZero(&ExtCurStyle->exts_globSubstrateTypes);
|
2019-09-19 02:48:33 +02:00
|
|
|
TTMaskZero(&ExtCurStyle->exts_globSubstrateShieldTypes);
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskSetMask(&ExtCurStyle->exts_globSubstrateTypes, &types1);
|
2019-09-19 02:48:33 +02:00
|
|
|
ExtCurStyle->exts_globSubstrateShieldTypes = idTypes;
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_globSubstratePlane = DBTechNoisyNamePlane(argv[2]);
|
2020-06-01 22:49:59 +02:00
|
|
|
|
2022-02-23 21:02:40 +01:00
|
|
|
/* The "default" substrate type is a type that is in the */
|
|
|
|
|
/* list of substrate types and exists on the substrate */
|
|
|
|
|
/* plane, where space on the same plane is also declared to */
|
|
|
|
|
/* be the substrate type. */
|
|
|
|
|
|
|
|
|
|
if (ExtCurStyle->exts_globSubstratePlane != -1)
|
|
|
|
|
{
|
|
|
|
|
TileType subType;
|
|
|
|
|
for (subType = TT_TECHDEPBASE; subType < DBNumUserLayers; subType++)
|
|
|
|
|
if (TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, subType))
|
|
|
|
|
if (DBPlane(subType) == ExtCurStyle->exts_globSubstratePlane)
|
|
|
|
|
{
|
|
|
|
|
ExtCurStyle->exts_globSubstrateDefaultType = subType;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-01 22:49:59 +02:00
|
|
|
/* Handle optional substrate node name */
|
|
|
|
|
if (argc == 4)
|
|
|
|
|
ExtCurStyle->exts_globSubstrateName = StrDup((char **)NULL, argv[3]);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case NOPLANEORDER: {
|
2020-05-23 23:13:14 +02:00
|
|
|
if ( ExtCurStyle->exts_planeOrderStatus == seenPlaneOrder )
|
2017-04-25 14:41:48 +02:00
|
|
|
TechError("\"noplaneordering\" specified after \"planeorder\".\n");
|
2020-05-23 23:13:14 +02:00
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
ExtCurStyle->exts_planeOrderStatus = noPlaneOrder ;
|
|
|
|
|
}
|
2024-12-07 02:51:59 +01:00
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case PLANEORDER: {
|
|
|
|
|
int pnum = (spointertype) dbTechNameLookup(argv[1], &dbPlaneNameLists);
|
|
|
|
|
int pos = atoi(argv[2]);
|
|
|
|
|
|
|
|
|
|
if ( ExtCurStyle->exts_planeOrderStatus == noPlaneOrder ) {
|
|
|
|
|
TechError("\"planeorder\" specified after \"noplaneordering\".\n");
|
|
|
|
|
}
|
|
|
|
|
ExtCurStyle->exts_planeOrderStatus = seenPlaneOrder ;
|
2020-05-23 23:13:14 +02:00
|
|
|
if (pnum < 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
TechError("Unknown planeorder plane %s\n", argv[1]);
|
|
|
|
|
else if (pos < 0 || pos >= DBNumPlanes-PL_TECHDEPBASE)
|
2020-05-23 23:13:14 +02:00
|
|
|
TechError("Planeorder index must be [0..%d]\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
DBNumPlanes-PL_TECHDEPBASE-1);
|
|
|
|
|
else
|
|
|
|
|
ExtCurStyle->exts_planeOrder[pnum] = pos;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return (TRUE);
|
|
|
|
|
|
|
|
|
|
usage:
|
|
|
|
|
TechError("Malformed line for keyword %s. Correct usage:\n\t%s %s\n",
|
|
|
|
|
kp->k_name, kp->k_name, kp->k_usage);
|
|
|
|
|
return (TRUE);
|
|
|
|
|
|
|
|
|
|
diffplane:
|
|
|
|
|
TechError("Overlapped types in \"sideoverlap\" rule must be on a\n"
|
|
|
|
|
"\tdifferent plane from intypes and outtypes.\n");
|
|
|
|
|
return (TRUE);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtTechFinal --
|
|
|
|
|
*
|
|
|
|
|
* Postprocess the technology specific information for extraction.
|
|
|
|
|
* Builds the connectivity tables exts_nodeConn[], exts_resistConn[],
|
2019-08-19 20:11:02 +02:00
|
|
|
* and exts_deviceConn[].
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Initializes the tables mentioned above.
|
|
|
|
|
* Leaves ExtCurStyle pointing to the first style in the list
|
|
|
|
|
* ExtAllStyles.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtTechFinal()
|
|
|
|
|
{
|
|
|
|
|
ExtStyle *es;
|
2022-01-14 23:30:05 +01:00
|
|
|
TileType s, t;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Create a "default" style if there isn't one */
|
|
|
|
|
if (ExtAllStyles == NULL)
|
|
|
|
|
{
|
|
|
|
|
ExtAllStyles = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
|
|
|
|
|
ExtAllStyles->exts_next = NULL;
|
|
|
|
|
ExtAllStyles->exts_name = StrDup((char **) NULL, "default");
|
|
|
|
|
|
|
|
|
|
ExtCurStyle = extTechStyleNew();
|
|
|
|
|
ExtCurStyle->exts_name = ExtAllStyles->exts_name;
|
|
|
|
|
ExtCurStyle->exts_status = TECH_LOADED;
|
|
|
|
|
}
|
|
|
|
|
extTechFinalStyle(ExtCurStyle);
|
2022-01-14 23:30:05 +01:00
|
|
|
|
|
|
|
|
/* Any type in the connection tables that is connected to */
|
|
|
|
|
/* something other than itself is added to the list of */
|
|
|
|
|
/* extractable types. */
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask mask;
|
|
|
|
|
TTMaskZero(&mask);
|
|
|
|
|
TTMaskSetMask(&mask, &DBConnectTbl[t]);
|
|
|
|
|
TTMaskClearType(&mask, t);
|
|
|
|
|
if (!TTMaskIsZero(&mask))
|
|
|
|
|
TTMaskSetType(allExtractTypes, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Any type that wasn't found anywhere in the extract section */
|
|
|
|
|
/* is considered non-electrical. */
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumUserLayers; s++)
|
|
|
|
|
if (!TTMaskHasType(allExtractTypes, s))
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("The following types are not handled by extraction and will"
|
|
|
|
|
" be treated as non-electrical types:\n");
|
|
|
|
|
TxPrintf(" ");
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
|
|
|
|
|
if (!TTMaskHasType(allExtractTypes, t))
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("%s ", DBTypeLongNameTbl[t]);
|
|
|
|
|
TTMaskClearType(&ExtCurStyle->exts_activeTypes, t);
|
|
|
|
|
}
|
|
|
|
|
TxPrintf("\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
extTechFinalStyle(style)
|
|
|
|
|
ExtStyle *style;
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask maskBits;
|
|
|
|
|
TileType r, s, t;
|
|
|
|
|
int p, p1, missing, conflict;
|
|
|
|
|
int indicis[NP];
|
|
|
|
|
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
{
|
|
|
|
|
maskBits = style->exts_nodeConn[r] = DBConnectTbl[r];
|
2019-08-19 20:11:02 +02:00
|
|
|
if (!TTMaskHasType(&style->exts_deviceMask, r))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
TTMaskZero(&style->exts_deviceConn[r]);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(&maskBits, s))
|
|
|
|
|
if (style->exts_typeToResistClass[s]
|
|
|
|
|
!= style->exts_typeToResistClass[r])
|
|
|
|
|
TTMaskClearType(&maskBits, s);
|
|
|
|
|
}
|
|
|
|
|
style->exts_resistConn[r] = maskBits;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* r ranges over types, s over resistance entries */
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
{
|
|
|
|
|
s = style->exts_typeToResistClass[r];
|
|
|
|
|
if (s >= 0)
|
|
|
|
|
TTMaskClearMask(&style->exts_typesResistChanged[r],
|
|
|
|
|
&style->exts_typesByResistClass[s]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Residue check:
|
|
|
|
|
* We have ignored all contact types when parsing parasitic
|
|
|
|
|
* capacitances. Now we need to add them. For each contact
|
|
|
|
|
* type, add the contact type to the types lists accordingly.
|
|
|
|
|
* Note that we don't have to record any cap values, since the
|
|
|
|
|
* extraction routine dissolves contacts into their residue
|
|
|
|
|
* types when computing the parasitics. But, the type must be
|
|
|
|
|
* in the type lists or contact tiles will be passed over during
|
|
|
|
|
* searches.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask rmask;
|
|
|
|
|
PlaneMask pMask;
|
|
|
|
|
TileType q;
|
|
|
|
|
|
|
|
|
|
if (!DBIsContact(s)) continue;
|
|
|
|
|
|
|
|
|
|
pMask = DBTypePlaneMaskTbl[s];
|
|
|
|
|
for (p = 0; p < DBNumPlanes; p++)
|
|
|
|
|
{
|
|
|
|
|
if (PlaneMaskHasPlane(pMask, p))
|
|
|
|
|
{
|
|
|
|
|
TTMaskSetType(&style->exts_overlapTypes[p], s);
|
|
|
|
|
TTMaskSetType(&style->exts_sideTypes[p], s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
DBFullResidueMask(s, &rmask);
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumUserLayers; r++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&rmask, r)) continue;
|
|
|
|
|
|
|
|
|
|
TTMaskSetMask(&style->exts_sideEdges[s], &style->exts_sideEdges[r]);
|
|
|
|
|
|
|
|
|
|
for (q = TT_TECHDEPBASE; q < DBNumUserLayers; q++)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
if (TTMaskHasType(&style->exts_overlapOtherTypes[q], r))
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskSetType(&style->exts_overlapOtherTypes[q], s);
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
|
|
|
|
|
if (TTMaskHasType(&style->exts_overlapShieldTypes[q][t], r)
|
|
|
|
|
&& !TTMaskHasType(&rmask, q)
|
|
|
|
|
&& !TTMaskHasType(&rmask, t))
|
|
|
|
|
TTMaskSetType(&style->exts_overlapShieldTypes[q][t], s);
|
|
|
|
|
|
|
|
|
|
/* For sideOverlap, t is "outtypes" and includes space, so we */
|
|
|
|
|
/* must count from TT_SPACE, not TT_TECHDEPBASE. */
|
|
|
|
|
|
|
|
|
|
for (t = TT_SPACE; t < DBNumUserLayers; t++)
|
2020-05-23 23:13:14 +02:00
|
|
|
if (TTMaskHasType(&style->exts_sideOverlapOtherTypes[q][t], r))
|
2017-04-25 14:41:48 +02:00
|
|
|
TTMaskSetType(&style->exts_sideOverlapOtherTypes[q][t], s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Consistency check:
|
|
|
|
|
* If a type R shields S from T, make sure that R is listed as
|
|
|
|
|
* being in the list of overlapped types for S, even if there
|
|
|
|
|
* was no overlap capacitance explicitly specified for this
|
|
|
|
|
* pair of types in an "overlap" line. This guarantees that
|
|
|
|
|
* R will shield S from substrate even if there is no capacitance
|
|
|
|
|
* associated with the overlap.
|
|
|
|
|
*/
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (style->exts_overlapShieldPlanes[s][t] == 0)
|
|
|
|
|
continue;
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
{
|
|
|
|
|
if (!TTMaskHasType(&style->exts_overlapShieldTypes[s][t], r))
|
|
|
|
|
continue;
|
|
|
|
|
p1 = DBPlane(s);
|
|
|
|
|
style->exts_overlapPlanes |= PlaneNumToMaskBit(p1);
|
|
|
|
|
style->exts_overlapOtherPlanes[s]
|
|
|
|
|
|= PlaneNumToMaskBit(DBPlane(r));
|
|
|
|
|
TTMaskSetType(&style->exts_overlapTypes[p1], s);
|
|
|
|
|
TTMaskSetType(&style->exts_overlapOtherTypes[s], r);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finally, for all coupling type masks, remove those types */
|
|
|
|
|
/* that have been declared not to participate in extraction. */
|
|
|
|
|
|
|
|
|
|
for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
TTMaskAndMask(&style->exts_overlapOtherTypes[s], &style->exts_activeTypes);
|
|
|
|
|
TTMaskAndMask(&style->exts_perimCapMask[s], &style->exts_activeTypes);
|
|
|
|
|
|
|
|
|
|
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
TTMaskAndMask(&style->exts_overlapShieldTypes[s][t],
|
2017-04-25 14:41:48 +02:00
|
|
|
&style->exts_activeTypes);
|
2020-05-23 23:13:14 +02:00
|
|
|
TTMaskAndMask(&style->exts_sideOverlapOtherTypes[s][t],
|
2017-04-25 14:41:48 +02:00
|
|
|
&style->exts_activeTypes);
|
|
|
|
|
TTMaskAndMask(&style->exts_sideCoupleOtherEdges[s][t],
|
|
|
|
|
&style->exts_activeTypes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (p = 0; p < DBNumPlanes; p++)
|
|
|
|
|
{
|
|
|
|
|
TTMaskAndMask(&style->exts_overlapTypes[p], &style->exts_activeTypes);
|
|
|
|
|
TTMaskAndMask(&style->exts_sideTypes[p], &style->exts_activeTypes);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if ( style->exts_planeOrderStatus == noPlaneOrder )
|
2017-04-25 14:41:48 +02:00
|
|
|
return /* no need to check */ ;
|
|
|
|
|
/* Else Check to make sure the plane order is a permutation of the
|
|
|
|
|
numbers 0..DBNumPlanes-DBNumPlanes-1 */
|
|
|
|
|
for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
|
|
|
|
|
indicis[p1] = 0;
|
|
|
|
|
}
|
|
|
|
|
for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
|
|
|
|
|
int pn = style->exts_planeOrder[p1]+PL_TECHDEPBASE;
|
|
|
|
|
if (pn >= PL_TECHDEPBASE && pn < DBNumPlanes)
|
|
|
|
|
indicis[pn]++;
|
|
|
|
|
}
|
|
|
|
|
conflict = FALSE;
|
|
|
|
|
missing = FALSE;
|
|
|
|
|
for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
|
|
|
|
|
if (indicis[p1] > 1) conflict = TRUE ;
|
|
|
|
|
if (indicis[p1] < 1) missing = TRUE ;
|
|
|
|
|
}
|
|
|
|
|
if (!conflict && !missing) /* Everything was ok */
|
2020-05-23 23:13:14 +02:00
|
|
|
goto zinit;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
TxError ("\nWarning: Extraction Style %s\n", style -> exts_name);
|
|
|
|
|
if (conflict) {
|
|
|
|
|
TxError (" Conflicting planeorder for plane(s):\n ");
|
|
|
|
|
for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
|
|
|
|
|
if (indicis[p1] > 1)
|
|
|
|
|
TxError (" %s,", DBPlaneLongNameTbl[p1]);
|
|
|
|
|
}
|
|
|
|
|
TxError("\n");
|
|
|
|
|
}
|
|
|
|
|
if (missing) {
|
|
|
|
|
TxError (" Missing planeorder for plane(s):\n ");
|
|
|
|
|
for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
|
|
|
|
|
if (indicis[p1] < 1)
|
|
|
|
|
TxError (" %s,", DBPlaneLongNameTbl[p1]);
|
|
|
|
|
}
|
|
|
|
|
TxError("\n");
|
|
|
|
|
}
|
|
|
|
|
TxError(" Magic will use the default planeorder for this style:\n ");
|
|
|
|
|
for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
|
|
|
|
|
style->exts_planeOrder[p1] = p1 - PL_TECHDEPBASE ;
|
|
|
|
|
TxError(" %s=%d,",DBPlaneLongNameTbl[p1], style->exts_planeOrder[p1]);
|
|
|
|
|
}
|
|
|
|
|
TxError("\n");
|
|
|
|
|
|
|
|
|
|
/* Now that we have a plane ordering, we can apply default height */
|
|
|
|
|
/* and thickness values for those layers. */
|
|
|
|
|
|
|
|
|
|
zinit:
|
|
|
|
|
for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
|
|
|
|
|
{
|
|
|
|
|
if (style->exts_thick[r] == 0)
|
|
|
|
|
style->exts_thick[r] = 0.05;
|
|
|
|
|
if (style->exts_height[r] == 0)
|
|
|
|
|
style->exts_height[r] = 0.1 * style->exts_planeOrder[DBPlane(r)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If global variable "doConvert" is TRUE, then we convert from */
|
2019-03-23 00:58:47 +01:00
|
|
|
/* microns to lambda and microns^2 to lambda^2. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (doConvert)
|
|
|
|
|
{
|
2019-03-23 00:58:47 +01:00
|
|
|
/* Use current CIF output scale for determining the scale */
|
|
|
|
|
/* factor between micron units in the extract section and */
|
|
|
|
|
/* lambda units of the database (conversion from lambda to */
|
|
|
|
|
/* internal units is done separately). */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-01-29 22:41:48 +01:00
|
|
|
float dscale = CIFGetOutputScale(1000);
|
2022-11-06 17:50:05 +01:00
|
|
|
float dsq = dscale * dscale;
|
2019-01-29 22:41:48 +01:00
|
|
|
|
|
|
|
|
CapValue scalefac = (CapValue)dscale;
|
2017-04-25 14:41:48 +02:00
|
|
|
CapValue sqfac = scalefac * scalefac;
|
|
|
|
|
|
|
|
|
|
for (r = 0; r < DBNumTypes; r++)
|
|
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
ExtDevice *devptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next)
|
2023-07-06 18:35:47 +02:00
|
|
|
{
|
|
|
|
|
ParamList *chkParam;
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
HashEntry *he;
|
|
|
|
|
ResValue res;
|
2023-07-06 18:35:47 +02:00
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
devptr->exts_deviceSDCap *= sqfac;
|
|
|
|
|
devptr->exts_deviceGateCap *= sqfac;
|
2023-07-06 18:35:47 +02:00
|
|
|
|
|
|
|
|
for (chkParam = devptr->exts_deviceParams; chkParam;
|
|
|
|
|
chkParam = chkParam->pl_next)
|
|
|
|
|
{
|
2023-10-25 02:29:04 +02:00
|
|
|
if (chkParam->pl_offset != 0)
|
|
|
|
|
{
|
|
|
|
|
if (chkParam->pl_param[0] == 'a')
|
|
|
|
|
chkParam->pl_offset /= dsq;
|
|
|
|
|
else
|
|
|
|
|
chkParam->pl_offset /= dscale;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 18:35:47 +02:00
|
|
|
if (chkParam->pl_minimum > chkParam->pl_maximum) continue;
|
|
|
|
|
else if (chkParam->pl_param[0] == 'a')
|
|
|
|
|
{
|
|
|
|
|
chkParam->pl_maximum /= dsq;
|
|
|
|
|
chkParam->pl_minimum /= dsq;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
chkParam->pl_maximum /= dscale;
|
|
|
|
|
chkParam->pl_minimum /= dscale;
|
|
|
|
|
}
|
|
|
|
|
}
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
|
|
|
|
|
he = HashLookOnly(&devptr->exts_deviceResist, "area");
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
res = (ResValue)(spointertype)(HashGetValue(he));
|
|
|
|
|
res /= dsq;
|
|
|
|
|
HashSetValue(he, (spointertype)res);
|
|
|
|
|
}
|
|
|
|
|
he = HashLookOnly(&devptr->exts_deviceResist, "perimeter");
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
res = (ResValue)(spointertype)(HashGetValue(he));
|
|
|
|
|
res /= dscale;
|
|
|
|
|
HashSetValue(he, (spointertype)res);
|
|
|
|
|
}
|
|
|
|
|
he = HashLookOnly(&devptr->exts_deviceResist, "linear");
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
{
|
|
|
|
|
res = (ResValue)(spointertype)(HashGetValue(he));
|
|
|
|
|
res /= dscale;
|
|
|
|
|
HashSetValue(he, (spointertype)res);
|
|
|
|
|
}
|
2019-08-19 20:11:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
style->exts_areaCap[r] *= sqfac;
|
2017-04-25 14:41:48 +02:00
|
|
|
for (s = 0; s < DBNumTypes; s++)
|
|
|
|
|
{
|
|
|
|
|
EdgeCap *ec;
|
|
|
|
|
|
|
|
|
|
style->exts_perimCap[r][s] *= scalefac;
|
|
|
|
|
style->exts_overlapCap[r][s] *= sqfac;
|
2023-01-27 17:47:37 +01:00
|
|
|
style->exts_overlapMult[r][s] *= scalefac;
|
2017-04-25 14:41:48 +02:00
|
|
|
for (ec = style->exts_sideOverlapCap[r][s]; ec != NULL;
|
|
|
|
|
ec = ec->ec_next)
|
|
|
|
|
ec->ec_cap *= scalefac;
|
|
|
|
|
|
|
|
|
|
// Note that because sidewall caps are referred to
|
|
|
|
|
// a specific distance, the value (run / separation)
|
|
|
|
|
// is unscaled, so the capacitance does not get
|
|
|
|
|
// modified by the scalefactor. However, the lambda
|
2020-05-23 23:13:14 +02:00
|
|
|
// reference for sidewall cap is 2 lambda, so if
|
2017-04-25 14:41:48 +02:00
|
|
|
// the reference is to be interpreted as 1 micron,
|
|
|
|
|
// the value needs to be divided by 2 (the factor of
|
|
|
|
|
// 2 is made up by the fact that the sidewall is
|
|
|
|
|
// independently accumulated on each plate of the
|
2022-05-28 16:33:21 +02:00
|
|
|
// capacitor). ALSO: ec_offset was multiplied up by
|
|
|
|
|
// 1000 so that micron distances could be saved as
|
|
|
|
|
// integer values, so that factor needs to be divided out.
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
for (ec = style->exts_sideCoupleCap[r][s]; ec != NULL;
|
|
|
|
|
ec = ec->ec_next)
|
2022-05-28 16:33:21 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
ec->ec_cap *= 0.5;
|
2023-02-22 21:30:50 +01:00
|
|
|
ec->ec_offset = (int)(((float)ec->ec_offset / dscale) + 0.5);
|
|
|
|
|
ec->ec_offset /= 1000;
|
2022-05-28 16:33:21 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-21 04:12:02 +02:00
|
|
|
|
|
|
|
|
/* Layer thickness and height are in microns, but are floating-point */
|
|
|
|
|
style->exts_thick[r] /= dscale;
|
|
|
|
|
style->exts_height[r] /= dscale;
|
2022-11-06 17:50:05 +01:00
|
|
|
|
|
|
|
|
/* Only this antenna coefficient has dimensioned units */
|
|
|
|
|
style->exts_antennaRatio[r].ratioDiffA *= dsq;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-17 22:35:41 +01:00
|
|
|
/* side halo, fringe shield halo, and step size are also in microns */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-01-29 22:41:48 +01:00
|
|
|
style->exts_sideCoupleHalo = (int)(((float)style->exts_sideCoupleHalo
|
|
|
|
|
/ dscale) + 0.5);
|
|
|
|
|
style->exts_stepSize = (int)(((float)style->exts_stepSize
|
|
|
|
|
/ dscale) + 0.5);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Avoid setting stepSize to zero, or extraction will hang! */
|
|
|
|
|
if (style->exts_stepSize <= 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: zero step size! Resetting to default.\n");
|
|
|
|
|
style->exts_stepSize = 100; /* Revert to default */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We had multiplied sideCoupleHalo by 1000 to accommodate a */
|
|
|
|
|
/* floating-point value in microns, whether or not doConvert was */
|
|
|
|
|
/* needed, so normalize it back to lambda units. */
|
|
|
|
|
|
|
|
|
|
style->exts_sideCoupleHalo /= 1000;
|
2023-07-06 18:35:47 +02:00
|
|
|
|
|
|
|
|
/* Ditto for the parameter maximum/minimum limits */
|
|
|
|
|
for (r = 0; r < DBNumTypes; r++)
|
|
|
|
|
{
|
|
|
|
|
ExtDevice *devptr;
|
|
|
|
|
ParamList *chkParam;
|
|
|
|
|
|
|
|
|
|
for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next)
|
|
|
|
|
{
|
|
|
|
|
for (chkParam = devptr->exts_deviceParams; chkParam;
|
|
|
|
|
chkParam = chkParam->pl_next)
|
|
|
|
|
{
|
2023-10-25 02:29:04 +02:00
|
|
|
if (chkParam->pl_offset != 0)
|
|
|
|
|
chkParam->pl_offset /= 1000;
|
2023-07-06 18:35:47 +02:00
|
|
|
if (chkParam->pl_minimum > chkParam->pl_maximum) continue;
|
|
|
|
|
chkParam->pl_maximum /= 1000;
|
|
|
|
|
chkParam->pl_minimum /= 1000;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* ExtTechScale --
|
|
|
|
|
*
|
|
|
|
|
* Scale all extraction values appropriately when rescaling the grid.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtTechScale(scalen, scaled)
|
|
|
|
|
int scalen; /* Scale numerator */
|
|
|
|
|
int scaled; /* Scale denominator */
|
|
|
|
|
{
|
|
|
|
|
ExtStyle *style = ExtCurStyle;
|
|
|
|
|
EdgeCap *ec;
|
|
|
|
|
int i, j;
|
|
|
|
|
float sqn, sqd;
|
|
|
|
|
|
|
|
|
|
if (style == NULL) return;
|
|
|
|
|
|
|
|
|
|
sqn = (float)(scalen * scalen);
|
|
|
|
|
sqd = (float)(scaled * scaled);
|
|
|
|
|
|
|
|
|
|
style->exts_unitsPerLambda = style->exts_unitsPerLambda * (float)scalen
|
|
|
|
|
/ (float)scaled;
|
|
|
|
|
DBScaleValue(&style->exts_sideCoupleHalo, scaled, scalen);
|
|
|
|
|
DBScaleValue(&style->exts_stepSize, scaled, scalen);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < DBNumTypes; i++)
|
|
|
|
|
{
|
2019-08-19 20:11:02 +02:00
|
|
|
ExtDevice *devptr;
|
2023-07-06 18:35:47 +02:00
|
|
|
ParamList *chkParam;
|
2019-08-19 20:11:02 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
style->exts_areaCap[i] *= sqn;
|
|
|
|
|
style->exts_areaCap[i] /= sqd;
|
|
|
|
|
|
2019-08-19 20:11:02 +02:00
|
|
|
for (devptr = style->exts_device[i]; devptr; devptr = devptr->exts_next)
|
|
|
|
|
{
|
|
|
|
|
devptr->exts_deviceSDCap *= sqn;
|
|
|
|
|
devptr->exts_deviceSDCap /= sqd;
|
|
|
|
|
devptr->exts_deviceGateCap *= sqn;
|
|
|
|
|
devptr->exts_deviceGateCap /= sqd;
|
2023-07-06 18:35:47 +02:00
|
|
|
|
|
|
|
|
for (chkParam = devptr->exts_deviceParams; chkParam;
|
|
|
|
|
chkParam = chkParam->pl_next)
|
|
|
|
|
{
|
2023-10-25 02:29:04 +02:00
|
|
|
if (chkParam->pl_offset != 0)
|
|
|
|
|
{
|
|
|
|
|
if (chkParam->pl_param[0] == 'a')
|
|
|
|
|
{
|
|
|
|
|
chkParam->pl_offset *= sqd;
|
|
|
|
|
chkParam->pl_offset /= sqn;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
chkParam->pl_offset *= scaled;
|
|
|
|
|
chkParam->pl_offset /= scalen;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-06 18:35:47 +02:00
|
|
|
if (chkParam->pl_minimum > chkParam->pl_maximum) continue;
|
|
|
|
|
else if (chkParam->pl_param[0] == 'a')
|
|
|
|
|
{
|
|
|
|
|
chkParam->pl_maximum *= sqd;
|
|
|
|
|
chkParam->pl_maximum /= sqn;
|
|
|
|
|
chkParam->pl_minimum *= sqd;
|
|
|
|
|
chkParam->pl_minimum /= sqn;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
chkParam->pl_maximum *= scaled;
|
|
|
|
|
chkParam->pl_maximum /= scalen;
|
|
|
|
|
chkParam->pl_minimum *= scaled;
|
|
|
|
|
chkParam->pl_minimum /= scalen;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-19 20:11:02 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
style->exts_height[i] *= scaled;
|
|
|
|
|
style->exts_height[i] /= scalen;
|
|
|
|
|
style->exts_thick[i] *= scaled;
|
|
|
|
|
style->exts_thick[i] /= scalen;
|
|
|
|
|
|
2022-11-06 17:50:05 +01:00
|
|
|
style->exts_antennaRatio[i].ratioDiffA *= sqn;
|
|
|
|
|
style->exts_antennaRatio[i].ratioDiffA /= sqd;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (j = 0; j < DBNumTypes; j++)
|
|
|
|
|
{
|
|
|
|
|
style->exts_perimCap[i][j] *= scalen;
|
|
|
|
|
style->exts_perimCap[i][j] /= scaled;
|
|
|
|
|
style->exts_overlapCap[i][j] *= sqn;
|
|
|
|
|
style->exts_overlapCap[i][j] /= sqd; /* Typo fixed in 7.2.57 */
|
2023-01-27 17:47:37 +01:00
|
|
|
style->exts_overlapMult[i][j] *= scalen;
|
|
|
|
|
style->exts_overlapMult[i][j] /= scaled;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
// Do not scale sidewall cap, for while the value is
|
|
|
|
|
// per distance, the distance is referred to a separation
|
|
|
|
|
// distance in the same units, so the cap never scales.
|
|
|
|
|
|
2022-05-28 16:33:21 +02:00
|
|
|
for (ec = style->exts_sideCoupleCap[i][j]; ec != NULL;
|
|
|
|
|
ec = ec->ec_next)
|
|
|
|
|
{
|
|
|
|
|
// ec->ec_cap *= scalen;
|
|
|
|
|
// ec->ec_cap /= scaled;
|
|
|
|
|
DBScaleValue(&(ec->ec_offset), scaled, scalen);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
for (ec = style->exts_sideOverlapCap[i][j]; ec != NULL;
|
|
|
|
|
ec = ec->ec_next)
|
|
|
|
|
{
|
|
|
|
|
ec->ec_cap *= scalen;
|
|
|
|
|
ec->ec_cap /= scaled;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|