/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: TechEditWizardData.java
* Create an Electric XML Technology from a simple numeric description of design rules
* Written in Perl by Andrew Wewist, translated to Java by Steven Rubin.
*
* Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.user.tecEditWizard;
import com.sun.electric.database.geometry.*;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.io.FileType;
import com.sun.electric.tool.io.IOTool;
import com.sun.electric.tool.user.dialogs.OpenFile;
import com.sun.electric.tool.user.User;
import com.sun.electric.technology.*;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import java.awt.Color;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
/**
* Class to handle the "Technology Creation Wizard" dialog.
*/
public class TechEditWizardData
{
private static final boolean POLY_CONTACT_SAME_AS_DIFF_CONTACT = false;
/************************************** THE DATA **************************************/
private String tech_name;
private String tech_description;
private int num_metal_layers;
private int stepsize; // value in nm
private double resolution; // technology resolution for delta values (not in real scale)
private boolean pSubstrateProcess = false; // to control if process is a pwell or psubstrate process or not. If true, Tech Creation Wizard will not create pwell layers
private boolean horizontalFlag = true; // to control if transistor gates are aligned horizontally. True by default . If transistors are horizontal -> M1 is horizontal?
// 0=Basic is only with metal info, 0=Standard is with info for mocmos, 1=Complex with extra elements like resistors
private int caseFlag = 0; // to control what information is stored: -1=basic, 0=standard, 1=complex
private boolean analogElementsFlag = false; // to control if analog elements are generated. False by default
private boolean secondPolyFlag = false; // to control if second poly. False by default
// DIFFUSION RULES
private WizardField diff_width = new WizardField();
private WizardField diff_poly_overhang = new WizardField(); // min. diff overhang from gate edge
private WizardField diff_contact_overhang = new WizardField(); // min. diff overhang contact
private WizardField diff_contact_overhang_min_short = new WizardField(); // diff overhang contact. It should hold the min short value
private WizardField diff_contact_overhang_min_long = new WizardField(); // diff overhang contact. It should hold the min long value
private WizardField diff_spacing = new WizardField();
// POLY RULES
private WizardField poly_width = new WizardField();
private WizardField poly_endcap = new WizardField(); // min. poly gate extension from edge of diffusion
private WizardField poly_spacing = new WizardField();
private WizardField poly_diff_spacing = new WizardField(); // min. spacing between poly and diffusion
private WizardField poly_protection_spacing = new WizardField(); // min. spacing between poly and dummy poly
// GATE RULES
private WizardField gate_length = new WizardField(); // min. transistor gate length
private WizardField gate_width = new WizardField(); // min. transistor gate width
private WizardField gate_spacing = new WizardField(); // min. gate to gate spacing on diffusion
private WizardField gate_contact_spacing = new WizardField(); // min. spacing from gate edge to contact inside diffusion
// Special rules for OD18 transistors if specified.
private WizardField gate_od18_length = new WizardField(); // transistor gate length for OD18 transistors
private WizardField gate_od18_width = new WizardField(); // transistor gate width for OD18 transistors
private WizardField[] od18_diff_overhang = new WizardField[]{new WizardField(), new WizardField()}; // OD18 X and Y overhang
// Special rules for native transistors if specified
private WizardField gate_nt_length = new WizardField(); // transistor gate length for native transistors
private WizardField gate_nt_width = new WizardField(); // transistor gate width for OD18 transistors
private WizardField poly_nt_endcap = new WizardField(); // gate extension from edge of diffusion for native transistors
private WizardField nt_diff_overhang = new WizardField(); // extension from OD
// Special rules for vth/vtl transistors if specified.
private WizardField vthl_diff_overhang = new WizardField(); // Overhang of VTH/VTL with respecto to OD
private WizardField vthl_poly_overhang = new WizardField(); // Overhang of VTH/VTL with respecto to the gate
// CONTACT RULES
private WizardField contact_size = new WizardField();
private WizardField contact_spacing = new WizardField();
private WizardField contact_array_spacing = new WizardField();
private WizardField contact_metal_overhang_inline_only = new WizardField(); // metal overhang when overhanging contact from two sides only
private WizardField contact_metal_overhang_all_sides = new WizardField(); // metal overhang when surrounding contact
private WizardField contact_poly_overhang = new WizardField(); // poly overhang contact. It should hold the recommended value
private WizardField polycon_diff_spacing = new WizardField(); // spacing between poly-metal contact edge and diffusion
// WELL AND IMPLANT RULES
private WizardField nplus_width = new WizardField();
private WizardField nplus_overhang_diff = new WizardField();
private WizardField nplus_overhang_strap = new WizardField(); // for well/substrate contact
private WizardField nplus_overhang_poly = new WizardField();
private WizardField nplus_spacing = new WizardField();
private WizardField pplus_width = new WizardField();
private WizardField pplus_overhang_diff = new WizardField();
private WizardField pplus_overhang_strap = new WizardField(); // for well/substrate contact
private WizardField pplus_overhang_poly = new WizardField();
private WizardField pplus_spacing = new WizardField();
private WizardField nwell_width = new WizardField();
private WizardField nwell_overhang_diff_p = new WizardField();
private WizardField nwell_overhang_diff_n = new WizardField();
private WizardField nwell_spacing = new WizardField();
// METAL RULES
private WizardField [] metal_width;
private WizardField [] metal_spacing;
private WizardField [] metal_minarea;
private WizardField [] metal_enclosedarea;
private List<WideWizardField> wide_metal_spacing = new ArrayList<WideWizardField>(); // For all wide spacing rules not displayed in graphcs
// VIA RULES
private WizardField [] via_size;
private WizardField [] via_inline_spacing;
private WizardField [] via_array_spacing;
private WizardField [] via_overhang;
// generic contacts
private static class LayerNode
{
String layer;
LayerNode(String l) {layer = l;}
}
private static class PolygonLayerNode extends LayerNode
{
List<EPoint> pList;
PolygonLayerNode(String l, List<EPoint> list)
{
super(l);
pList = list;
}
}
// generic rectangular contacts
private static class RectLayerNode extends LayerNode
{
WizardField valueX; // overhang or size X value. Size value for cuts
WizardField valueY; // overhang or size Y value. Size value for cuts
RectLayerNode(String l, double valX, String nameX, double valY, String nameY)
{
super(l);
valueX = new WizardField(valX, nameX);
valueY = new WizardField(valY, nameY);
}
}
private static class Contact
{
// some primitives might not have prefix. "-" should not be in the prefix to avoid
// being displayed in the palette
String prefix;
List<LayerNode> layers;
// odd metals go vertical
Contact (String p)
{
prefix = p;
layers = new ArrayList<LayerNode>();
}
}
private Map<String,List<Contact>> metalContacts = new HashMap<String,List<Contact>>();
private Map<String,List<Contact>> otherContacts = new HashMap<String,List<Contact>>();
private Map<String,List<Contact>> noMultiContacts = new HashMap<String,List<Contact>>();
private Map<String,List<Contact>> generalContacts = new HashMap<String,List<Contact>>();
private Map<String,List<Contact>> capacitors = new HashMap<String,List<Contact>>();
private static class PaletteGroup
{
String name;
List<Xml.ArcProto> arcs;
List<Xml.MenuNodeInst> pins;
List<Xml.MenuNodeInst> elements; // contact or transistor
void addArc(Xml.ArcProto arc)
{
if (arcs == null)
{
arcs = new ArrayList<Xml.ArcProto>();
}
arcs.add(arc);
}
private void add(List<Xml.MenuNodeInst> list, Xml.PrimitiveNodeGroup element, String shortName)
{
assert element.isSingleton;
Xml.PrimitiveNode pn = element.nodes.get(0);
Xml.MenuNodeInst n = new Xml.MenuNodeInst();
n.protoName = pn.name;
n.function = pn.function;
if (shortName != null)
{
n.text = shortName;
}
list.add(n);
}
void addPinOrResistor(Xml.PrimitiveNodeGroup pin, String shortName)
{
if (pins == null)
{
pins = new ArrayList<Xml.MenuNodeInst>();
}
add(pins, pin, shortName);
}
void addElement(Xml.PrimitiveNodeGroup element, String shortName)
{
if (elements == null)
{
elements = new ArrayList<Xml.MenuNodeInst>();
}
add(elements, element, shortName);
}
}
// ANTENNA RULES
private double poly_antenna_ratio;
private double [] metal_antenna_ratio;
// GDS-II LAYERS
public static class LayerInfo
{
String name;
int value; // normal gds value
int type; // datatype of the normal gds value
int pin; // gds pin value
int pinType; // gds pin datatype
int text; // gds text value
int textType; // gds text datatype
String cif; // cif value
String graphicsTemplate; // uses other template for the graphics
Color graphicsColor; // uses this color with no fill
EGraphics.Outline graphicsOutline; // uses this outline with graphicsColor
int [] graphicsPattern; // uses this pattern with graphicsColor
double width = 0; // width of the pure layer node to create
double height = -1; // height for 3D view
double thickness = -1; // thickness for 3D view
boolean addArc = false;
WizardField spacing, minimum;
Layer.Function function = Layer.Function.ART; // ART is better default than UNKNOWN
PaletteGroup grp; // to place extra elements for a given layer later in the palette
LayerInfo(String n)
{
name = n;
}
String getValueWithType() {return (type != 0) ? value + "/" + type : value + "";}
String getPinWithType() {return (pinType != 0) ? pin + "/" + pinType : pin + "";}
String getTextWithType() {return (textType != 0) ? text + "/" + textType : text + "";}
void setGDSData(int[] vals)
{
assert(vals.length == 6);
value = vals[0];
type = vals[1];
pin = vals[2];
pinType = vals[3];
text = vals[4];
textType = vals[5];
}
void setLayerInformation(String s)
{
StringTokenizer p = new StringTokenizer(s, ":", false);
while (p.hasMoreTokens())
{
String str = p.nextToken();
// Remove white spaces
// str = TextUtils.trimLeading(str);
str = str.trim(); // leading and trailing white spaces
int index = str.indexOf("=");
if (str.startsWith("G"))
{
setGDSData(getGDSValuesFromString(str.substring(index+1)));
}
else if (str.startsWith("W")) // width
{
assert(index != -1);
width = Double.parseDouble(str.substring(index+1));
}
else if (str.startsWith("T")) // thick for 3D View
{
assert(index != -1);
thickness = Double.parseDouble(str.substring(index+1));
}
else if (str.startsWith("H")) // height for 3D View
{
assert(index != -1);
height = Double.parseDouble(str.substring(index+1));
}
else if (str.startsWith("S")) // spacing rule
{
assert(index != -1);
spacing = new WizardField();
fillRule(str.substring(index+1), "{/}", spacing);
}
else if (str.startsWith("M")) // spacing rule
{
assert(index != -1);
minimum = new WizardField();
fillRule(str.substring(index+1), "{/}", minimum);
}
else if (str.startsWith("A")) // for arcs
{
assert (str.length() == 1 && str.toLowerCase().equals("a")); // It must be A
addArc = true;
}
else if (str.startsWith("F")) // function
{
assert(index != -1);
function = Layer.Function.valueOf(str.substring(index+1));
}
else if (str.startsWith("CIF")) // CIF
{
assert(index != -1);
cif = str.substring(index+1);
}
else if (str.startsWith("C")) // color
{
assert(index != -1);
setGraphicsTemplate(str.substring(index+1));
}
else
{
System.out.println("Case not implemented");
assert(false);
}
}
}
void setGraphicsTemplate(String s)
{
if (s.startsWith("[")) // color
{
StringTokenizer p = new StringTokenizer(s, ". []", false);
int[] colors = new int[3];
String outlineOrPattern = null; // EGraphics.Outline.NOPAT.name(); // default
int itemCount = 0;
while (p.hasMoreTokens())
{
String str = p.nextToken();
if (itemCount < 3)
colors[itemCount++] = Integer.parseInt(str);
else
outlineOrPattern = str;
assert(itemCount < 4);
}
EGraphics.Outline outline = EGraphics.Outline.findOutline(EGraphics.Outline.NOPAT.name());
int[] pattern = new int[16];
graphicsColor = new Color(colors[0], colors[1], colors[2]);
if (outlineOrPattern != null)
{
EGraphics.Outline out = EGraphics.Outline.findOutline(outlineOrPattern);
if (out != null) // manages to parse a valid Outline
outline = out;
else
{
assert(outlineOrPattern.startsWith("{"));
// Pattern information
StringTokenizer pat = new StringTokenizer(outlineOrPattern, "/ {}", false);
int count = 0;
while (pat.hasMoreTokens())
{
String str = pat.nextToken();
int num = Integer.parseInt(str);
assert(count < 16);
pattern[count++] = num;
}
if (count != 16)
assert(count == 16);
}
}
graphicsOutline = outline;
graphicsPattern = pattern;
}
else
graphicsTemplate = s;
}
public String toString()
{
String val = getValueWithType(); // useful datatype
if (pin != 0)
{
val = val + "," + getPinWithType() + "p";
}
if (text != 0)
{
val = val + "," + getTextWithType() + "t";
}
return val;
}
}
private LayerInfo diff_layer = new LayerInfo("Diff");
private LayerInfo poly_layer = new LayerInfo("Poly");
private LayerInfo poly2_layer = new LayerInfo("Polysilicon-2");
private LayerInfo nplus_layer = new LayerInfo("NPlus");
private LayerInfo pplus_layer = new LayerInfo("PPlus");
private LayerInfo nwell_layer = new LayerInfo("N-Well");
private LayerInfo contact_layer = new LayerInfo("Contact");
private LayerInfo [] metal_layers;
private LayerInfo [] via_layers;
private LayerInfo marking_layer = new LayerInfo("DeviceMark"); // Device marking layer
private List<LayerInfo> extraLayers; // extra layers
LayerInfo[] getBasicLayers()
{
List<LayerInfo> layers = new ArrayList<LayerInfo>();
layers.add(diff_layer);
layers.add(poly_layer);
layers.addAll(extraLayers); // nothing added if list is empty
layers.add(nplus_layer);
layers.add(pplus_layer);
layers.add(nwell_layer);
layers.add(contact_layer);
layers.add(marking_layer);
LayerInfo[] array = new LayerInfo[layers.size()];
layers.toArray(array);
return array;
}
public TechEditWizardData()
{
stepsize = 100;
num_metal_layers = 2;
allocateVariables();
}
private void allocateVariables()
{
// Garbage collector will deal with previouly allocated variables.
// Not worth it to clear them
metal_width = new WizardField[num_metal_layers];
metal_spacing = new WizardField[num_metal_layers];
metal_minarea = new WizardField[num_metal_layers];
metal_enclosedarea = new WizardField[num_metal_layers];
via_size = new WizardField[num_metal_layers-1];
via_inline_spacing = new WizardField[num_metal_layers-1];
via_array_spacing = new WizardField[num_metal_layers-1];
via_overhang = new WizardField[num_metal_layers-1];
metal_antenna_ratio = new double[num_metal_layers];
metal_layers = new LayerInfo[num_metal_layers];
via_layers = new LayerInfo[num_metal_layers-1];
for(int i=0; i<num_metal_layers; i++)
{
metal_width[i] = new WizardField();
metal_spacing[i] = new WizardField();
metal_minarea[i] = new WizardField();
metal_enclosedarea[i] = new WizardField();
metal_layers[i] = new LayerInfo("Metal-"+(i+1));
}
for(int i=0; i<num_metal_layers-1; i++)
{
via_size[i] = new WizardField();
via_inline_spacing[i] = new WizardField();
via_array_spacing[i] = new WizardField();
via_overhang[i] = new WizardField();
via_layers[i] = new LayerInfo("Via-"+(i+1));
}
// extra layers
extraLayers = new ArrayList<LayerInfo>();
}
/************************************** ACCESSOR METHODS **************************************/
String getTechName() { return tech_name; }
void setTechName(String s) { tech_name = s; }
String getTechDescription() { return tech_description; }
void setTechDescription(String s) { tech_description = s; }
int getStepSize() { return stepsize; }
void setStepSize(int n) { stepsize = n; }
double getResolution() { return resolution; }
void setResolution(double n) { resolution = n; }
int getNumMetalLayers() { return num_metal_layers; }
void setNumMetalLayers(int n)
{
if (n < 1)
{
System.out.println("Setting zero as number of metal layers");
return; // nothing to do
}
int smallest = Math.min(n, num_metal_layers);
// n size arrays
WizardField [] new_metal_width = new WizardField[n];
WizardField [] new_metal_spacing = new WizardField[n];
WizardField [] new_metal_minarea = new WizardField[n];
WizardField [] new_metal_enclosedarea = new WizardField[n];
double [] new_metal_antenna_ratio = new double[n];
for(int i=0; i<smallest; i++)
{
new_metal_width[i] = metal_width[i];
new_metal_spacing[i] = metal_spacing[i];
new_metal_minarea[i] = metal_minarea[i];
new_metal_enclosedarea[i] = metal_enclosedarea[i];
new_metal_antenna_ratio[i] = metal_antenna_ratio[i];
}
for(int i=smallest; i<n; i++)
{
new_metal_width[i] = new WizardField();
new_metal_spacing[i] = new WizardField();
new_metal_minarea[i] = new WizardField();
new_metal_enclosedarea[i] = new WizardField();
}
metal_width = new_metal_width;
metal_spacing = new_metal_spacing;
metal_minarea = new_metal_minarea;
metal_enclosedarea = new_metal_enclosedarea;
metal_antenna_ratio = new_metal_antenna_ratio;
// n-1 size arrays
WizardField [] new_via_size = new WizardField[n-1];
WizardField [] new_via_spacing = new WizardField[n-1];
WizardField [] new_via_array_spacing = new WizardField[n-1];
WizardField [] new_via_overhang_inline = new WizardField[n-1];
for(int i=0; i<smallest-1; i++)
{
new_via_size[i] = via_size[i];
new_via_spacing[i] = via_inline_spacing[i];
new_via_array_spacing[i] = via_array_spacing[i];
new_via_overhang_inline[i] = via_overhang[i];
}
for(int i=smallest-1; i<n-1; i++)
{
new_via_size[i] = new WizardField();
new_via_spacing[i] = new WizardField();
new_via_array_spacing[i] = new WizardField();
new_via_overhang_inline[i] = new WizardField();
}
via_size = new_via_size;
via_inline_spacing = new_via_spacing;
via_array_spacing = new_via_array_spacing;
via_overhang = new_via_overhang_inline;
LayerInfo [] new_gds_metal_layer = new LayerInfo[n];
for(int i=0; i<smallest; i++)
{
new_gds_metal_layer[i] = metal_layers[i];
}
for(int i=smallest-1; i<n; i++)
{
new_gds_metal_layer[i] = new LayerInfo("Metal-"+(i+1));
}
metal_layers = new_gds_metal_layer;
LayerInfo [] new_gds_via_layer = new LayerInfo[n-1];
for(int i=0; i<smallest-1; i++) new_gds_via_layer[i] = via_layers[i];
for(int i=smallest-1; i<n-1; i++) new_gds_via_layer[i] = new LayerInfo("Via-"+(i+1));
via_layers = new_gds_via_layer;
num_metal_layers = n;
}
// Flags
boolean getPSubstratelProcess() { return pSubstrateProcess;}
void setPSubstratelProcess(boolean b) { pSubstrateProcess = b; }
boolean getHorizontalTransistors() { return horizontalFlag;}
void setHorizontalTransistors(boolean b) { horizontalFlag = b; }
private boolean isComplexCase() { return caseFlag == 1;} // complex case
private boolean isBasicCase() { return caseFlag == -1;} // basic case = only metals
private void setCaseFlag(int b) { assert(b > -2 && b < 2); caseFlag = b; } // -1, 0, 1
private boolean getAnalogFlag() { return analogElementsFlag;}
private void setAnalogFlag(boolean b) { analogElementsFlag = b; }
private boolean getSecondPolyFlag() { return secondPolyFlag;}
private void setSecondPolyFlag(boolean b) { secondPolyFlag = b; }
// DIFFUSION RULES
WizardField getDiffWidth() { return diff_width; }
void setDiffWidth(WizardField v) { diff_width = v; }
WizardField getDiffPolyOverhang() { return diff_poly_overhang; }
void setDiffPolyOverhang(WizardField v) { diff_poly_overhang = v; }
WizardField getDiffContactOverhang() { return diff_contact_overhang; }
void setDiffContactOverhang(WizardField v) { diff_contact_overhang = v; }
WizardField getDiffSpacing() { return diff_spacing; }
void setDiffSpacing(WizardField v) { diff_spacing = v; }
// POLY RULES
WizardField getPolyWidth() { return poly_width; }
void setPolyWidth(WizardField v) { poly_width = v; }
WizardField getPolyEndcap() { return poly_endcap; }
void setPolyEndcap(WizardField v) { poly_endcap = v; }
WizardField getPolySpacing() { return poly_spacing; }
void setPolySpacing(WizardField v) { poly_spacing = v; }
WizardField getPolyDiffSpacing() { return poly_diff_spacing; }
void setPolyDiffSpacing(WizardField v) { poly_diff_spacing = v; }
WizardField getPolyProtectionSpacing() { return poly_protection_spacing; }
void setPolyProtectionSpacing(WizardField v) { poly_protection_spacing = v; }
// GATE RULES
WizardField getGateLength() { return gate_length; }
void setGateLength(WizardField v) { gate_length = v; }
WizardField getGateWidth() { return gate_width; }
void setGateWidth(WizardField v) { gate_width = v; }
WizardField getGateSpacing() { return gate_spacing; }
void setGateSpacing(WizardField v) { gate_spacing = v; }
WizardField getGateContactSpacing() { return gate_contact_spacing; }
void setGateContactSpacing(WizardField v) { gate_contact_spacing = v; }
// CONTACT RULES
WizardField getContactSize() { return contact_size; }
void setContactSize(WizardField v) { contact_size = v; }
WizardField getContactSpacing() { return contact_spacing; }
void setContactSpacing(WizardField v) { contact_spacing = v; }
WizardField getContactArraySpacing() { return contact_array_spacing; }
void setContactArraySpacing(WizardField v) { contact_array_spacing = v; }
WizardField getContactMetalOverhangInlineOnly() { return contact_metal_overhang_inline_only; }
void setContactMetalOverhangInlineOnly(WizardField v) { contact_metal_overhang_inline_only = v; }
WizardField getContactMetalOverhangAllSides() { return contact_metal_overhang_all_sides; }
void setContactMetalOverhangAllSides(WizardField v) { contact_metal_overhang_all_sides = v; }
WizardField getContactPolyOverhang() { return contact_poly_overhang; }
void setContactPolyOverhang(WizardField v) { contact_poly_overhang = v; }
WizardField getPolyconDiffSpacing() { return polycon_diff_spacing; }
void setPolyconDiffSpacing(WizardField v) { polycon_diff_spacing = v; }
// WELL AND IMPLANT RULES
WizardField getNPlusWidth() { return nplus_width; }
void setNPlusWidth(WizardField v) { nplus_width = v; }
WizardField getNPlusOverhangDiff() { return nplus_overhang_diff; }
void setNPlusOverhangDiff(WizardField v) { nplus_overhang_diff = v; }
WizardField getNPlusOverhangStrap() { return nplus_overhang_strap; }
void setNPlusOverhangStrap(WizardField v) { nplus_overhang_strap = v; }
WizardField getNPlusOverhangPoly() { return nplus_overhang_poly; }
void setNPlusOverhangPoly(WizardField v) { nplus_overhang_poly = v; }
WizardField getNPlusSpacing() { return nplus_spacing; }
void setNPlusSpacing(WizardField v) { nplus_spacing = v; }
WizardField getPPlusWidth() { return pplus_width; }
void setPPlusWidth(WizardField v) { pplus_width = v; }
WizardField getPPlusOverhangDiff() { return pplus_overhang_diff; }
void setPPlusOverhangDiff(WizardField v) { pplus_overhang_diff = v; }
WizardField getPPlusOverhangStrap() { return pplus_overhang_strap; }
void setPPlusOverhangStrap(WizardField v) { pplus_overhang_strap = v; }
WizardField getPPlusOverhangPoly() { return pplus_overhang_poly; }
void setPPlusOverhangPoly(WizardField v) { pplus_overhang_poly = v; }
WizardField getPPlusSpacing() { return pplus_spacing; }
void setPPlusSpacing(WizardField v) { pplus_spacing = v; }
WizardField getNWellWidth() { return nwell_width; }
void setNWellWidth(WizardField v) { nwell_width = v; }
WizardField getNWellOverhangDiffP() { return nwell_overhang_diff_p; }
void setNWellOverhangDiffP(WizardField v) { nwell_overhang_diff_p = v; }
WizardField getNWellOverhangDiffN() { return nwell_overhang_diff_n; }
void setNWellOverhangDiffN(WizardField v) { nwell_overhang_diff_n = v; }
WizardField getNWellSpacing() { return nwell_spacing; }
void setNWellSpacing(WizardField v) { nwell_spacing = v; }
// METAL RULES
WizardField [] getMetalWidth() { return metal_width; }
void setMetalWidth(int met, WizardField value) { metal_width[met] = value; }
WizardField [] getMetalSpacing() { return metal_spacing; }
void setMetalSpacing(int met, WizardField value) { metal_spacing[met] = value; }
// VIA RULES
WizardField [] getViaSize() { return via_size; }
void setViaSize(int via, WizardField value) { via_size[via] = value; }
WizardField [] getViaSpacing() { return via_inline_spacing; }
void setViaSpacing(int via, WizardField value) { via_inline_spacing[via] = value; }
WizardField [] getViaArraySpacing() { return via_array_spacing; }
void setViaArraySpacing(int via, WizardField value) { via_array_spacing[via] = value; }
WizardField [] getViaOverhangInline() { return via_overhang; }
void setViaOverhangInline(int via, WizardField value) { via_overhang[via] = value; }
// ANTENNA RULES
public double getPolyAntennaRatio() { return poly_antenna_ratio; }
void setPolyAntennaRatio(double v) { poly_antenna_ratio = v; }
public double [] getMetalAntennaRatio() { return metal_antenna_ratio; }
void setMetalAntennaRatio(int met, double value) { metal_antenna_ratio[met] = value; }
// GDS-II LAYERS
static int[] getGDSValuesFromString(String s)
{
int[] vals = new int[6];
StringTokenizer parse = new StringTokenizer(s, ",", false);
while (parse.hasMoreTokens())
{
String v = parse.nextToken();
int pos = 0;
int index = v.indexOf("/");
if (v.contains("p")) // pin section
{
pos = 2;
} else if (v.contains("t")) // text section
{
pos = 4;
}
if (index != -1) // datatype value
{
vals[pos] = TextUtils.atoi(v.substring(0, index));
vals[pos+1] = TextUtils.atoi(v.substring(index+1));
}
else
vals[pos] = TextUtils.atoi(v);
}
return vals;
}
TechEditWizardData.LayerInfo [] getGDSMetal() { return metal_layers; }
TechEditWizardData.LayerInfo [] getGDSVia() { return via_layers; }
private String errorInData()
{
// check the General data
if (tech_name == null || tech_name.length() == 0) return "General panel: No technology name";
if (stepsize == 0) return "General panel: Invalid unit size";
if (!isBasicCase()) // at least standard case
{
// check the Active data
if (diff_width.value == 0) return "Active panel: Invalid width";
// check the Poly data
if (poly_width.value == 0) return "Poly panel: Invalid width";
// check the Gate data
if (gate_width.value == 0) return "Gate panel: Invalid width";
if (gate_length.value == 0) return "Gate panel: Invalid length";
// check the Contact data
if (contact_size.value == 0) return "Contact panel: Invalid size";
// check the Well/Implant data
if (nplus_width.value == 0) return "Well/Implant panel: Invalid NPlus width";
if (pplus_width.value == 0) return "Well/Implant panel: Invalid PPlus width";
if (nwell_width.value == 0) return "Well/Implant panel: Invalid NWell width";
}
// check the Metal data
for(int i=0; i<num_metal_layers; i++)
if (metal_width[i].value == 0) return "Metal panel: Invalid Metal-" + (i+1) + " width";
// check the Via data
for(int i=0; i<num_metal_layers-1; i++)
if (via_size[i].value == 0) return "Via panel: Invalid Via-" + (i+1) + " size";
return null;
}
/************************************** IMPORT RAW NUMBERS FROM DISK **************************************/
/**
* Method to import data from a file to this object.
* @return true on success; false on failure.
*/
boolean importData()
{
String fileName = OpenFile.chooseInputFile(FileType.ANY, "Technology Wizard File");
if (fileName == null) return false;
return importData(fileName);
}
/**
* Method to import data from a given file to this object. It is also in the regression so
* keep the access.
* @param fileName the name of the file to import.
* @return true on success; false on failure.
*/
public boolean importData(String fileName)
{
URL url = TextUtils.makeURLToFile(fileName);
// clean containers before reading the next txt file
allocateVariables();
try
{
URLConnection urlCon = url.openConnection();
InputStreamReader is = new InputStreamReader(urlCon.getInputStream());
LineNumberReader lineReader = new LineNumberReader(is);
boolean validFile = false;
for(;;)
{
String buf = lineReader.readLine();
if (buf == null) break;
buf = buf.trim();
if (buf.length() == 0 || buf.startsWith("#")) continue;
// parse the assignment
// not a valid line
if (!(buf.startsWith("$") || buf.startsWith("@")))
continue; // keep reading
validFile = true;
int spacePos = buf.indexOf(' ');
int equalsPos = buf.indexOf('=');
if (equalsPos < 0)
{
Job.getUserInterface().showErrorMessage("Missing '=' on line " + lineReader.getLineNumber(),
"Syntax Error In Technology File");
break;
}
if (spacePos < 0) spacePos = equalsPos; else
spacePos = Math.min(spacePos, equalsPos);
String varName = buf.substring(1, spacePos);
boolean single = buf.startsWith("$");
int semiPos = buf.indexOf(';');
if (semiPos < 0)
{
Job.getUserInterface().showErrorMessage("Missing ';' on line " + lineReader.getLineNumber(),
"Syntax Error In Technology File");
break;
}
equalsPos++;
while (equalsPos < semiPos && buf.charAt(equalsPos) == ' ') equalsPos++;
String varValue = buf.substring(equalsPos, semiPos);
// now figure out what to assign
if (varName.equalsIgnoreCase("tech_libname")) { } else
if (varName.equalsIgnoreCase("tech_name")) setTechName(stripQuotes(varValue)); else
if (varName.equalsIgnoreCase("tech_description")) setTechDescription(stripQuotes(varValue)); else
if (varName.equalsIgnoreCase("num_metal_layers")) setNumMetalLayers(TextUtils.atoi(varValue)); else
if (varName.equalsIgnoreCase("psubstrate_process")) setPSubstratelProcess(Boolean.valueOf(varValue)); else
if (varName.equalsIgnoreCase("horizontal_transistors")) setHorizontalTransistors(Boolean.valueOf(varValue)); else
if (varName.equalsIgnoreCase("extra_info")) setCaseFlag(Integer.valueOf(varValue)); else
if (varName.equalsIgnoreCase("stepsize")) setStepSize(TextUtils.atoi(varValue)); else
if (varName.equalsIgnoreCase("resolution")) setResolution(TextUtils.atof(varValue)); else
if (varName.equalsIgnoreCase("analog_elements")) setAnalogFlag(Boolean.valueOf(varValue)); else
if (varName.equalsIgnoreCase("second_poly")) setSecondPolyFlag(Boolean.valueOf(varValue)); else
if (varName.equalsIgnoreCase("diff_width")) diff_width.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("diff_width_rule")) diff_width.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("diff_poly_overhang")) diff_poly_overhang.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("diff_poly_overhang_rule")) diff_poly_overhang.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("diff_contact_overhang")) diff_contact_overhang.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("diff_contact_overhang_rule")) diff_contact_overhang.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("diff_contact_overhang_short_min")) diff_contact_overhang_min_short.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("diff_contact_overhang_short_min_rule")) diff_contact_overhang_min_short.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("diff_contact_overhang_long_min")) diff_contact_overhang_min_long.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("diff_contact_overhang_long_min_rule")) diff_contact_overhang_min_long.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("diff_spacing")) diff_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("diff_spacing_rule")) diff_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("poly_width")) poly_width.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("poly_width_rule")) poly_width.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("poly_endcap")) poly_endcap.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("poly_endcap_rule")) poly_endcap.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("poly_spacing")) poly_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("poly_spacing_rule")) poly_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("poly_diff_spacing")) poly_diff_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("poly_diff_spacing_rule")) poly_diff_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("poly_protection_spacing")) poly_protection_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("poly_protection_spacing_rule")) poly_protection_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("gate_length")) gate_length.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("gate_length_rule")) gate_length.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("gate_width")) gate_width.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("gate_width_rule")) gate_width.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("gate_spacing")) gate_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("gate_spacing_rule")) gate_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("gate_contact_spacing")) gate_contact_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("gate_contact_spacing_rule")) gate_contact_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("gate_od18_length")) fillRule(varValue, gate_od18_length); else
if (varName.equalsIgnoreCase("gate_od18_width")) fillRule(varValue, gate_od18_width); else
if (varName.equalsIgnoreCase("od18_diff_overhang")) fillRule(varValue, od18_diff_overhang); else
if (varName.equalsIgnoreCase("gate_nt_length")) fillRule(varValue, gate_nt_length); else
if (varName.equalsIgnoreCase("gate_nt_width")) fillRule(varValue, gate_nt_width); else
if (varName.equalsIgnoreCase("poly_nt_endcap")) fillRule(varValue, poly_nt_endcap); else
if (varName.equalsIgnoreCase("nt_diff_overhang")) fillRule(varValue, nt_diff_overhang); else
if (varName.equalsIgnoreCase("vthl_diff_overhang")) fillRule(varValue, vthl_diff_overhang); else
if (varName.equalsIgnoreCase("vthl_poly_overhang")) fillRule(varValue, vthl_poly_overhang); else
if (varName.equalsIgnoreCase("contact_size")) contact_size.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("contact_size_rule")) contact_size.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("contact_spacing")) contact_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("contact_spacing_rule")) contact_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("contact_array_spacing")) contact_array_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("contact_array_spacing_rule")) contact_array_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("contact_metal_overhang_inline_only")) contact_metal_overhang_inline_only.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("contact_metal_overhang_inline_only_rule")) contact_metal_overhang_inline_only.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("contact_metal_overhang_all_sides")) contact_metal_overhang_all_sides.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("contact_metal_overhang_all_sides_rule")) contact_metal_overhang_all_sides.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("contact_poly_overhang")) contact_poly_overhang.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("contact_poly_overhang_rule")) contact_poly_overhang.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("polycon_diff_spacing")) polycon_diff_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("polycon_diff_spacing_rule")) polycon_diff_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nplus_width")) nplus_width.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nplus_width_rule")) nplus_width.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nplus_overhang_diff")) nplus_overhang_diff.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nplus_overhang_diff_rule")) nplus_overhang_diff.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nplus_overhang_strap")) nplus_overhang_strap.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nplus_overhang_strap_rule")) nplus_overhang_strap.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nplus_overhang_poly")) nplus_overhang_poly.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nplus_overhang_poly_rule")) nplus_overhang_poly.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nplus_spacing")) nplus_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nplus_spacing_rule")) nplus_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("pplus_width")) pplus_width.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("pplus_width_rule")) pplus_width.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("pplus_overhang_diff")) pplus_overhang_diff.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("pplus_overhang_diff_rule")) pplus_overhang_diff.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("pplus_overhang_strap")) pplus_overhang_strap.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("pplus_overhang_strap_rule")) pplus_overhang_strap.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("pplus_overhang_poly")) pplus_overhang_poly.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("pplus_overhang_poly_rule")) pplus_overhang_poly.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("pplus_spacing")) pplus_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("pplus_spacing_rule")) pplus_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nwell_width")) nwell_width.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nwell_width_rule")) nwell_width.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nwell_overhang_diff_p")) nwell_overhang_diff_p.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nwell_overhang_diff_rule_p")) nwell_overhang_diff_p.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nwell_overhang_diff_n")) nwell_overhang_diff_n.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nwell_overhang_diff_rule_n")) nwell_overhang_diff_n.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("nwell_spacing")) nwell_spacing.value = TextUtils.atof(varValue); else
if (varName.equalsIgnoreCase("nwell_spacing_rule")) nwell_spacing.rule = stripQuotes(varValue); else
if (varName.equalsIgnoreCase("metal_width")) fillWizardArray(varValue, metal_width, false); else
if (varName.equalsIgnoreCase("metal_width_rule")) fillWizardArray(varValue, metal_width, true); else
if (varName.equalsIgnoreCase("metal_spacing")) fillWizardArray(varValue, metal_spacing, false); else
if (varName.equalsIgnoreCase("metal_spacing_rule")) fillWizardArray(varValue, metal_spacing, true); else
if (varName.equalsIgnoreCase("wide_metal_spacing_rules")) fillWizardWideArray(varValue, wide_metal_spacing); else
if (varName.equalsIgnoreCase("metal_minarea_rules")) fillRulesSeries(varValue, metal_minarea); else
if (varName.equalsIgnoreCase("metal_enclosed_area_rules")) fillRulesSeries(varValue, metal_enclosedarea); else
// private void fillRulesSeries(String str, WizardField... rules)
if (varName.equalsIgnoreCase("via_size")) fillWizardArray(varValue, via_size, false); else
if (varName.equalsIgnoreCase("via_size_rule")) fillWizardArray(varValue, via_size, true); else
if (varName.equalsIgnoreCase("via_spacing")) fillWizardArray(varValue, via_inline_spacing, false); else
if (varName.equalsIgnoreCase("via_spacing_rule")) fillWizardArray(varValue, via_inline_spacing, true); else
if (varName.equalsIgnoreCase("via_array_spacing")) fillWizardArray(varValue, via_array_spacing, false); else
if (varName.equalsIgnoreCase("via_array_spacing_rule")) fillWizardArray(varValue, via_array_spacing, true); else
if (varName.equalsIgnoreCase("via_overhang_inline")) fillWizardArray(varValue, via_overhang, false); else
if (varName.equalsIgnoreCase("via_overhang_inline_rule")) fillWizardArray(varValue, via_overhang, true); else
if (varName.equalsIgnoreCase("metal_contacts_series")) fillContactSeries(varValue, metalContacts); else
if (varName.equalsIgnoreCase("contacts_series")) fillContactSeries(varValue, otherContacts); else
// capacitors
if (varName.equalsIgnoreCase("capacitors_series")) fillContactSeries(varValue, capacitors); else
// more generic contacts that are not multicuts.
if (varName.equalsIgnoreCase("nomulti_contacts_series")) fillContactSeries(varValue, noMultiContacts); else
// contacts defined by a set of polygons and not multicuts
if (varName.equalsIgnoreCase("general_contacts_series")) fillPolygonalContactSeries(varValue, generalContacts); else
// Special layers
if (varName.equalsIgnoreCase("extra_layers")) fillLayerSeries(varValue, extraLayers); else
if (varName.equalsIgnoreCase("poly_antenna_ratio")) setPolyAntennaRatio(TextUtils.atof(varValue)); else
if (varName.equalsIgnoreCase("metal_antenna_ratio")) metal_antenna_ratio = makeDoubleArray(varValue); else
if (varName.equalsIgnoreCase("gds_diff_layer")) diff_layer.setGDSData(getGDSValuesFromString(varValue)); else
if (varName.equalsIgnoreCase("gds_poly_layer")) poly_layer.setGDSData(getGDSValuesFromString(varValue)); else
if (varName.equalsIgnoreCase("gds_nplus_layer")) nplus_layer.setGDSData(getGDSValuesFromString(varValue)); else
if (varName.equalsIgnoreCase("gds_pplus_layer")) pplus_layer.setGDSData(getGDSValuesFromString(varValue)); else
if (varName.equalsIgnoreCase("gds_nwell_layer")) nwell_layer.setGDSData(getGDSValuesFromString(varValue)); else
if (varName.equalsIgnoreCase("gds_contact_layer")) contact_layer.setGDSData(getGDSValuesFromString(varValue)); else
if (varName.equalsIgnoreCase("gds_metal_layer")) metal_layers = setGDSDataArray(varValue, num_metal_layers, "Metal-"); else
if (varName.equalsIgnoreCase("gds_via_layer")) via_layers = setGDSDataArray(varValue, num_metal_layers - 1, "Via-"); else
if (varName.equalsIgnoreCase("gds_marking_layer")) marking_layer.setGDSData(getGDSValuesFromString(varValue));
else
{
Object obj = null;
if (single)
{
WizardField wf = findWizardField(varName);
if (wf != null)
{
fillRule(varValue, wf);
}
obj = wf;
}
else
{
// two rules in the definition
WizardRuleFields wfs = findWizardRuleFields(varName);
if (wfs != null)
{
fillRules(varValue, wfs);
}
obj = wfs;
}
if (obj == null)
{
Job.getUserInterface().showErrorMessage("Unknown keyword '" + varName + "' on line " + lineReader.getLineNumber(),
"Syntax Error In Technology File");
break;
}
}
}
if (!validFile)
{
Job.getUserInterface().showErrorMessage("'" + fileName + "' is not a valid Parameters file. Check content.", "Syntax Error In Technology File");
}
lineReader.close();
} catch (IOException e)
{
System.out.println("Error reading " + fileName);
return false;
}
return true;
}
private static List<WizardField> extraVariables = new ArrayList<WizardField>();
private WizardField findWizardField(String varName)
{
for (WizardField wf : extraVariables)
{
if (wf.name.equals(varName))
return wf;
}
// it doesn't exist yet. Adding it now.
WizardField w = new WizardField(varName);
extraVariables.add(w);
return w;
}
private static List<WizardRuleFields> extraDoubleFields = new ArrayList<WizardRuleFields>();
private WizardRuleFields findWizardRuleFields(String varName)
{
for (WizardRuleFields wf : extraDoubleFields)
{
if (wf.name.equals(varName))
return wf;
}
// it doesn't exist yet. Adding it now.
WizardRuleFields w = new WizardRuleFields(varName);
extraDoubleFields.add(w);
return w;
}
// @TODO stripQuotes is a weak function since it will fail with '"something" ' (white spaces after
// the second "
private String stripQuotes(String str)
{
if (str.startsWith("\"") && str.endsWith("\""))
return str.substring(1, str.length()-1);
return str;
}
private LayerInfo [] setGDSDataArray(String str, int len, String extra)
{
LayerInfo [] foundArray = new LayerInfo[len];
for(int i=0; i<len; i++) foundArray[i] = new LayerInfo(extra + (i+1));
StringTokenizer parse = new StringTokenizer(str, "( \")", false);
int count = 0;
while (parse.hasMoreTokens())
{
if (count >= len)
{
System.out.println("More GDS values than metal layers in TechEditWizardData");
break;
}
else
{
String value = parse.nextToken();
// array delimeters must be discarded here because GDS string may
// contain "," for the pin/text definition ("," can't be used in the StringTokenizer
if (!value.equals(","))
foundArray[count++].setGDSData(getGDSValuesFromString(value));
}
}
return foundArray;
}
private double [] makeDoubleArray(String str)
{
WizardField [] foundArray = new WizardField[num_metal_layers];
for(int i=0; i<num_metal_layers; i++) foundArray[i] = new WizardField();
fillWizardArray(str, foundArray, false);
double [] retArray = new double[foundArray.length];
for(int i=0; i<foundArray.length; i++)
retArray[i] = foundArray[i].value;
return retArray;
}
private void fillWizardWideArray(String str, List<WideWizardField> wideList)
{
StringTokenizer parse = new StringTokenizer(str, "[]", false);
int blocks = 0;
WideWizardField tmp = new WideWizardField();
while (parse.hasMoreTokens())
{
String value = parse.nextToken();
if (value.equals(";")) // end of line
continue;
// first block: [value, maxW, minLen, rule name]
if (blocks == 0)
{
StringTokenizer p = new StringTokenizer(value, ", ", false);
int count = 0;
while (p.hasMoreTokens())
{
String v = p.nextToken();
switch (count)
{
case 0: // value
tmp.value = Double.parseDouble(v);
break;
case 1: // maxW
tmp.maxW = Double.parseDouble(v);
break;
case 2: // minLen
tmp.minLen = Double.parseDouble(v);
break;
case 3: // rule name
tmp.rule = stripQuotes(v);
break;
default:
assert(false); // only 4 values
}
count++;
}
blocks++;
}
else
{
// layers involved
StringTokenizer p = new StringTokenizer(value, ",", false);
while (p.hasMoreTokens())
{
String s = p.nextToken();
tmp.names.add(s);
}
}
}
wideList.add(tmp);
}
private void fillWizardArray(String str, WizardField[] fieldArray, boolean getRule)
{
if (!str.startsWith("("))
{
Job.getUserInterface().showErrorMessage("Array does not start with '(' on " + str,
"Syntax Error In Technology File");
return;
}
StringTokenizer parse = new StringTokenizer(str, "(),\"", false);
int index = 0;
while (parse.hasMoreTokens())
{
String value = parse.nextToken().trim();
if (value.equals("")) continue; // just white space
if (getRule) // assune "<name>" format
{
fieldArray[index++].rule = value;
}
else
{
double v = TextUtils.atof(value);
fieldArray[index++].value = v;
}
}
// int pos = 1;
// for(;;)
// {
// while (pos < str.length() && str.charAt(pos) == ' ') pos++;
//
// if (index >= fieldArray.length)
// {
//// Job.getUserInterface().showErrorMessage("Invalid metal index: " + index,
//// "Syntax Error In Technology File");
// return;
// }
// if (getRule)
// {
//// if (str.charAt(pos) != '"')
//// {
//// Job.getUserInterface().showErrorMessage("Rule element does not start with quote on " + str,
//// "Syntax Error In Technology File");
//// return;
//// }
//// pos++;
//// int end = pos;
//// while (end < str.length() && str.charAt(end) != '"') end++;
//// if (str.charAt(end) != '"')
//// {
//// Job.getUserInterface().showErrorMessage("Rule element does not end with quote on " + str,
//// "Syntax Error In Technology File");
//// return;
//// }
// fieldArray[index++].rule = eatQuotes(str, pos);// str.substring(pos, end);
// pos = end+1;
// } else
// {
// double v = TextUtils.atof(str.substring(pos));
// fieldArray[index++].value = v;
// }
// while (pos < str.length() && str.charAt(pos) != ',' && str.charAt(pos) != ')') pos++;
// if (str.charAt(pos) != ',') break;
// pos++;
// }
}
// fillRules
private static void fillRules(String str, WizardRuleFields rule)
{
fillRule(str, "(,)", rule.xValue, rule.yValue);
}
// fillRule
private static void fillRule(String str, WizardField... rules)
{
fillRule(str, "(,)", rules);
}
private static void fillRule(String str, String tokens, WizardField... rules)
{
StringTokenizer parse = new StringTokenizer(str, tokens, false);
int count = 0;
int pos = 0;
while (parse.hasMoreTokens())
{
String value = parse.nextToken();
switch (count)
{
case 0:
case 2:
rules[pos].value = Double.parseDouble(value);
break;
case 1:
case 3:
value = value.replaceAll("\"", ""); // remove extra quotes
rules[pos].rule = value;
break;
default:
assert(false); // only 2 values
}
count++;
if (count == 2) pos++;
}
}
// fillLayerSeries
private void fillLayerSeries(String str, List<LayerInfo> layersList)
{
StringTokenizer parse = new StringTokenizer(str, "()", false);
while (parse.hasMoreTokens())
{
String value = parse.nextToken();
if (!value.contains(",")) // only white space
continue;
// Sequence ("layer name", "GDS value", "[Display data]", "{A|P,W=?}")
StringTokenizer p = new StringTokenizer(value, ",", false);
// can't use space as token because of possible DRC rules
int itemCount = 0; // 2 max items: layer name and GDS value
LayerInfo layer = null;
while (p.hasMoreTokens())
{
String s = p.nextToken();
if (s.startsWith(",")) continue; // skipping comma. Not in the parser because of the color
switch (itemCount)
{
case 0:
layer = new LayerInfo(s);
layersList.add(layer);
break;
case 1:
case 2:
case 3:
layer.setLayerInformation(s);
break;
default:
assert(false);
}
itemCount++;
}
assert(itemCount > 1 && itemCount < 5); // 2, 3 or 3
}
}
// to get serie of drc rules described as a set [(value1, "rule name 1") .... (valueN, "rule name N")]
private void fillRulesSeries(String str, WizardField... rules)
{
int index = str.indexOf("(");
if (!str.startsWith("[") || index == -1)
{
Job.getUserInterface().showErrorMessage("Array does not start with '[' on " + str,
"Syntax Error In Technology File");
return;
}
StringTokenizer parse = new StringTokenizer(str.substring(index), "[]", false);
// it should be only 1 set of []. Keeping the two loops for consistency and no need of
// detecting ']' as special case
while (parse.hasMoreTokens())
{
String value = parse.nextToken();
if (value.equals(";") || value.startsWith(" ")) // end of line or eats white spaces btw ()
continue;
// syntax: (value1, "name1") ... (valueN, "nameN")
StringTokenizer p = new StringTokenizer(value, "()", false);
int pos = 0;
while (p.hasMoreTokens())
{
String s = p.nextToken();
if (s.equals(";") || s.startsWith(" ")) // end of line or eats white spaces btw ()
continue;
// layer info
int itemCount = 0; // 2 max items: value, rule name
StringTokenizer x = new StringTokenizer(s, ", \"", false);
if (pos >= rules.length)
assert(pos < rules.length); // no out of bound array
while (x.hasMoreTokens() && itemCount < 2)
{
String item = x.nextToken();
switch (itemCount)
{
case 0: // rule value
rules[pos].value = Double.valueOf(item);
break;
case 1: // rule name
rules[pos++].rule = item;
break;
}
itemCount++;
}
if (itemCount != 2)
assert(itemCount == 2);
}
}
}
// to get most of the rectangular contact series including the non-multi cuts
private void fillContactSeries(String str, Map<String, List<Contact>> contactMap)
{
StringTokenizer parse = new StringTokenizer(str, "[]", false);
List<RectLayerNode> nodeList = new ArrayList<RectLayerNode>();
while (parse.hasMoreTokens())
{
String value = parse.nextToken();
if (value.equals(";")) // end of line
continue;
// checking the metal pair lists.
// overhang values should be in by now
if (value.contains("{"))
{
assert(nodeList.size() > 0);
int index = value.indexOf("{");
assert(index != -1); // it should come with a prefix name
String prefix = value.substring(0, index);
String v = value.substring(index);
StringTokenizer p = new StringTokenizer(v, "{}", false);
while (p.hasMoreTokens())
{
String pair = p.nextToken();
// getting metal numbers {a,b,c}
StringTokenizer n = new StringTokenizer(pair, ", ", false); // getting the layer names
List<String> layerNames = new ArrayList<String>();
while (n.hasMoreTokens())
{
String l = n.nextToken();
layerNames.add(l);
}
assert (nodeList.size() == layerNames.size());
Contact cont = new Contact(prefix);
for (int i = 0; i < layerNames.size(); i++)
{
String name = layerNames.get(i);
RectLayerNode tmp = nodeList.get(i);
RectLayerNode node = new RectLayerNode(name,
tmp.valueX.value, tmp.valueX.rule,
tmp.valueY.value, tmp.valueY.rule);
cont.layers.add(node);
}
String layer1 = layerNames.get(0);
String layer2 = layerNames.get(1); // n/p plus regions should go at the end
// Always store them by lowMetal-highMetal if happens
if (layer1.compareToIgnoreCase(layer2) > 0) // layer1 name is second
{
String temp = layer1;
layer1 = layer2;
layer2 = temp;
}
String key = layer1 + "-" + layer2;
List<Contact> l = contactMap.get(key);
if (l == null)
{
l = new ArrayList<Contact>();
contactMap.put(key, l);
}
l.add(cont);
}
}
else
{
// syntax: (overX, overXS, overY, overYS)(overX, overXS, overY, overYS)
// pair of layers found
StringTokenizer p = new StringTokenizer(value, "()", false);
while (p.hasMoreTokens())
{
String s = p.nextToken();
// layer info
int itemCount = 0; // 4 max items: metal layer, overhang X, overhang X rule, overhang Y, overhang Y rule
StringTokenizer x = new StringTokenizer(s, ", ", false);
double overX = 0, overY = 0;
String overXS = null, overYS = null;
while (x.hasMoreTokens() && itemCount < 4)
{
String item = x.nextToken();
switch (itemCount)
{
case 0: // overhang X value
overX = Double.valueOf(item);
break;
case 1: // overhang X rule name
overXS = item;
break;
case 2: // overhang Y value
overY = Double.valueOf(item);
break;
case 3: // overhang Y rule name
overYS = item;
break;
}
itemCount++;
}
assert(itemCount == 4);
RectLayerNode node = new RectLayerNode("", overX, overXS, overY, overYS);
nodeList.add(node);
}
}
}
}
// to read contacts defined by set of points. The first set defines the angles.
private void fillPolygonalContactSeries(String str, Map<String, List<Contact>> contactMap)
{
StringTokenizer parse = new StringTokenizer(str, "{}", false);
int count = 0;
double[] points = new double[2];
List<PolygonLayerNode> nodeList = new ArrayList<PolygonLayerNode>();
while (parse.hasMoreTokens())
{
String value = parse.nextToken();
if (value.equals(";")) // end of line
continue;
StringTokenizer par = new StringTokenizer(value, "[]", false);
while (par.hasMoreTokens())
{
String val = par.nextToken();
List<EPoint> pList = new ArrayList<EPoint>();
// they should come in pairs!
count++;
if (count%2 == 1) // odd
{
// point definition (X,Y)
StringTokenizer p = new StringTokenizer(val, "()", false);
while (p.hasMoreTokens())
{
String v = p.nextToken();
StringTokenizer g = new StringTokenizer(v, ", ", false);
int index = 0;
points[0] = points[1] = Double.MIN_VALUE;
while (g.hasMoreTokens())
{
assert(index < 2);
points[index++] = Double.parseDouble(g.nextToken());
}
assert(index == 2);
EPoint point = new EPoint(points[0], points[1]);
pList.add(point);
}
}
else // even -> layer definition
{
// Reading layer names
StringTokenizer n = new StringTokenizer(val, ", ", false);
while (n.hasMoreTokens())
{
String l = n.nextToken();
PolygonLayerNode node = new PolygonLayerNode(l, pList); // copy or just share the list?
nodeList.add(node);
}
}
}
}
// generalContacts.add(con);
}
/************************************** EXPORT RAW NUMBERS TO DISK **************************************/
void exportData()
{
String fileName = OpenFile.chooseOutputFile(FileType.TEXT, "Technology Wizard File", "Technology.txt");
if (fileName == null) return;
try
{
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
dumpNumbers(printWriter);
printWriter.close();
} catch (IOException e)
{
System.out.println("Error writing XML file '" + fileName + "'");
return;
}
}
private void dumpNumbers(PrintWriter pw)
{
pw.print("#### Electric(tm) VLSI Design System, version ");
if (User.isIncludeDateAndVersionInOutput())
{
pw.println(com.sun.electric.database.text.Version.getVersion());
} else
{
pw.println();
}
pw.println("#### ");
pw.println("#### Technology wizard data file");
pw.println("####");
pw.println("#### All dimensions in nanometers.");
if (IOTool.isUseCopyrightMessage())
{
String str = IOTool.getCopyrightMessage();
int start = 0;
while (start < str.length())
{
int endPos = str.indexOf('\n', start);
if (endPos < 0) endPos = str.length();
String oneLine = str.substring(start, endPos);
pw.println("#### " + oneLine);
start = endPos+1;
}
}
pw.println();
pw.println("$tech_name = \"" + tech_name + "\";");
pw.println("$tech_description = \"" + tech_description + "\";");
pw.println("$num_metal_layers = " + num_metal_layers + ";");
pw.println("$psubstrate_process = " + pSubstrateProcess + ";");
pw.println("$horizontal_transistors = " + horizontalFlag + ";");
pw.println("$extra_info = " + caseFlag + ";");
pw.println();
pw.println("## stepsize is minimum granularity that will be used as movement grid");
pw.println("## set to manufacturing grid or lowest common denominator with design rules");
pw.println("$stepsize = " + stepsize + ";");
pw.println();
pw.println("###### DIFFUSION RULES #####");
pw.println("$diff_width = " + TextUtils.formatDouble(diff_width.value) + ";");
pw.println("$diff_width_rule = \"" + diff_width.rule + "\";");
pw.println("$diff_poly_overhang = " + TextUtils.formatDouble(diff_poly_overhang.value) + "; # min. diff overhang from gate edge");
pw.println("$diff_poly_overhang_rule = \"" + diff_poly_overhang.rule + "\"; # min. diff overhang from gate edge");
pw.println("$diff_contact_overhang = " + TextUtils.formatDouble(diff_contact_overhang.value) + "; # min. diff overhang contact");
pw.println("$diff_contact_overhang_rule = \"" + diff_contact_overhang.rule + "\"; # min. diff overhang contact");
pw.println("$diff_spacing = " + TextUtils.formatDouble(diff_spacing.value) + ";");
pw.println("$diff_spacing_rule = \"" + diff_spacing.rule + "\";");
pw.println();
pw.println("###### POLY RULES #####");
pw.println("$poly_width = " + TextUtils.formatDouble(poly_width.value) + ";");
pw.println("$poly_width_rule = \"" + poly_width.rule + "\";");
pw.println("$poly_endcap = " + TextUtils.formatDouble(poly_endcap.value) + "; # min. poly gate extension from edge of diffusion");
pw.println("$poly_endcap_rule = \"" + poly_endcap.rule + "\"; # min. poly gate extension from edge of diffusion");
pw.println("$poly_spacing = " + TextUtils.formatDouble(poly_spacing.value) + ";");
pw.println("$poly_spacing_rule = \"" + poly_spacing.rule + "\";");
pw.println("$poly_diff_spacing = " + TextUtils.formatDouble(poly_diff_spacing.value) + "; # min. spacing between poly and diffusion");
pw.println("$poly_diff_spacing_rule = \"" + poly_diff_spacing.rule + "\"; # min. spacing between poly and diffusion");
pw.println("$poly_protection_spacing = " + TextUtils.formatDouble(poly_protection_spacing.value) + "; # min. spacing between poly and dummy poly");
pw.println("$poly_protection_spacing_rule = \"" + poly_protection_spacing.rule + "\"; # min. spacing between poly and dummy poly");
pw.println();
pw.println("###### GATE RULES #####");
pw.println("$gate_length = " + TextUtils.formatDouble(gate_length.value) + "; # min. transistor gate length");
pw.println("$gate_length_rule = \"" + gate_length.rule + "\"; # min. transistor gate length");
pw.println("$gate_width = " + TextUtils.formatDouble(gate_width.value) + "; # min. transistor gate width");
pw.println("$gate_width_rule = \"" + gate_width.rule + "\"; # min. transistor gate width");
pw.println("$gate_spacing = " + TextUtils.formatDouble(gate_spacing.value) + "; # min. gate to gate spacing on diffusion");
pw.println("$gate_spacing_rule = \"" + gate_spacing.rule + "\"; # min. gate to gate spacing on diffusion");
pw.println("$gate_contact_spacing = " + TextUtils.formatDouble(gate_contact_spacing.value) + "; # min. spacing from gate edge to contact inside diffusion");
pw.println("$gate_contact_spacing_rule = \"" + gate_contact_spacing.rule + "\"; # min. spacing from gate edge to contact inside diffusion");
pw.println();
pw.println("###### CONTACT RULES #####");
pw.println("$contact_size = " + TextUtils.formatDouble(contact_size.value) + ";");
pw.println("$contact_size_rule = \"" + contact_size.rule + "\";");
pw.println("$contact_spacing = " + TextUtils.formatDouble(contact_spacing.value) + ";");
pw.println("$contact_spacing_rule = \"" + contact_spacing.rule + "\";");
pw.println("$contact_array_spacing = " + TextUtils.formatDouble(contact_array_spacing.value) + ";");
pw.println("$contact_array_spacing_rule = \"" + contact_array_spacing.rule + "\";");
pw.println("$contact_metal_overhang_inline_only = " + TextUtils.formatDouble(contact_metal_overhang_inline_only.value) + "; # metal overhang when overhanging contact from two sides only");
pw.println("$contact_metal_overhang_inline_only_rule = \"" + contact_metal_overhang_inline_only.rule + "\"; # metal overhang when overhanging contact from two sides only");
pw.println("$contact_metal_overhang_all_sides = " + TextUtils.formatDouble(contact_metal_overhang_all_sides.value) + "; # metal overhang when surrounding contact");
pw.println("$contact_metal_overhang_all_sides_rule = \"" + contact_metal_overhang_all_sides.rule + "\"; # metal overhang when surrounding contact");
pw.println("$contact_poly_overhang = " + TextUtils.formatDouble(contact_poly_overhang.value) + "; # poly overhang contact, recommended value");
pw.println("$contact_poly_overhang_rule = \"" + contact_poly_overhang.rule + "\"; # poly overhang contact, recommended value");
pw.println("$polycon_diff_spacing = " + TextUtils.formatDouble(polycon_diff_spacing.value) + "; # spacing between poly-metal contact edge and diffusion");
pw.println("$polycon_diff_spacing_rule = \"" + polycon_diff_spacing.rule + "\"; # spacing between poly-metal contact edge and diffusion");
pw.println();
pw.println("###### WELL AND IMPLANT RULES #####");
pw.println("$nplus_width = " + TextUtils.formatDouble(nplus_width.value) + ";");
pw.println("$nplus_width_rule = \"" + nplus_width.rule + "\";");
pw.println("$nplus_overhang_diff = " + TextUtils.formatDouble(nplus_overhang_diff.value) + ";");
pw.println("$nplus_overhang_diff_rule = \"" + nplus_overhang_diff.rule + "\";");
pw.println("$nplus_overhang_strap = " + TextUtils.formatDouble(nplus_overhang_strap.value) + ";");
pw.println("$nplus_overhang_strap_rule = \"" + nplus_overhang_strap.rule + "\";");
pw.println("$nplus_overhang_poly = " + TextUtils.formatDouble(nplus_overhang_poly.value) + ";");
pw.println("$nplus_overhang_poly_rule = \"" + nplus_overhang_poly.rule + "\";");
pw.println("$nplus_spacing = " + TextUtils.formatDouble(nplus_spacing.value) + ";");
pw.println("$nplus_spacing_rule = \"" + nplus_spacing.rule + "\";");
pw.println();
pw.println("$pplus_width = " + TextUtils.formatDouble(pplus_width.value) + ";");
pw.println("$pplus_width_rule = \"" + pplus_width.rule + "\";");
pw.println("$pplus_overhang_diff = " + TextUtils.formatDouble(pplus_overhang_diff.value) + ";");
pw.println("$pplus_overhang_diff_rule = \"" + pplus_overhang_diff.rule + "\";");
pw.println("$pplus_overhang_strap = " + TextUtils.formatDouble(pplus_overhang_strap.value) + ";");
pw.println("$pplus_overhang_strap_rule = \"" + pplus_overhang_strap.rule + "\";");
pw.println("$pplus_overhang_poly = " + TextUtils.formatDouble(pplus_overhang_poly.value) + ";");
pw.println("$pplus_overhang_poly_rule = \"" + pplus_overhang_poly.rule + "\";");
pw.println("$pplus_spacing = " + TextUtils.formatDouble(pplus_spacing.value) + ";");
pw.println("$pplus_spacing_rule = \"" + pplus_spacing.rule + "\";");
pw.println();
pw.println("$nwell_width = " + TextUtils.formatDouble(nwell_width.value) + ";");
pw.println("$nwell_width_rule = \"" + nwell_width.rule + "\";");
pw.println("$nwell_overhang_diff_p = " + TextUtils.formatDouble(nwell_overhang_diff_p.value) + ";");
pw.println("$nwell_overhang_diff_rule_p = \"" + nwell_overhang_diff_p.rule + "\";");
pw.println("$nwell_overhang_diff_n = " + TextUtils.formatDouble(nwell_overhang_diff_n.value) + ";");
pw.println("$nwell_overhang_diff_rule_n = \"" + nwell_overhang_diff_n.rule + "\";");
pw.println("$nwell_spacing = " + TextUtils.formatDouble(nwell_spacing.value) + ";");
pw.println("$nwell_spacing_rule = \"" + nwell_spacing.rule + "\";");
pw.println();
pw.println("###### METAL RULES #####");
pw.print("@metal_width = (");
for(int i=0; i<num_metal_layers; i++)
{
if (i > 0) pw.print(", ");
pw.print(TextUtils.formatDouble(metal_width[i].value));
}
pw.println(");");
pw.print("@metal_width_rule = (");
for(int i=0; i<num_metal_layers; i++)
{
if (i > 0) pw.print(", ");
pw.print("\"" + metal_width[i].rule + "\"");
}
pw.println(");");
pw.print("@metal_spacing = (");
for(int i=0; i<num_metal_layers; i++)
{
if (i > 0) pw.print(", ");
pw.print(TextUtils.formatDouble(metal_spacing[i].value));
}
pw.println(");");
pw.print("@metal_spacing_rule = (");
for(int i=0; i<num_metal_layers; i++)
{
if (i > 0) pw.print(", ");
pw.print("\"" + metal_spacing[i].rule + "\"");
}
pw.println(");");
pw.println();
pw.println("###### VIA RULES #####");
pw.print("@via_size = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print(TextUtils.formatDouble(via_size[i].value));
}
pw.println(");");
pw.print("@via_size_rule = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print("\"" + via_size[i].rule + "\"");
}
pw.println(");");
pw.print("@via_spacing = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print(TextUtils.formatDouble(via_inline_spacing[i].value));
}
pw.println(");");
pw.print("@via_spacing_rule = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print("\"" + via_inline_spacing[i].rule + "\"");
}
pw.println(");");
pw.println();
pw.println("## \"sep2d\" spacing, close proximity via array spacing");
pw.print("@via_array_spacing = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print(TextUtils.formatDouble(via_array_spacing[i].value));
}
pw.println(");");
pw.print("@via_array_spacing_rule = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print("\"" + via_array_spacing[i].rule + "\"");
}
pw.println(");");
pw.print("@via_overhang_inline = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print(TextUtils.formatDouble(via_overhang[i].value));
}
pw.println(");");
pw.print("@via_overhang_inline_rule = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print("\"" + via_overhang[i].rule + "\"");
}
pw.println(");");
pw.println();
pw.println("###### ANTENNA RULES #####");
pw.println("$poly_antenna_ratio = " + TextUtils.formatDouble(poly_antenna_ratio) + ";");
pw.print("@metal_antenna_ratio = (");
for(int i=0; i<num_metal_layers; i++)
{
if (i > 0) pw.print(", ");
pw.print(TextUtils.formatDouble(metal_antenna_ratio[i]));
}
pw.println(");");
pw.println();
pw.println("###### GDS-II LAYERS #####");
pw.println("$gds_diff_layer = " + diff_layer + ";");
pw.println("$gds_poly_layer = " + poly_layer + ";");
pw.println("$gds_nplus_layer = " + nplus_layer + ";");
pw.println("$gds_pplus_layer = " + pplus_layer + ";");
pw.println("$gds_nwell_layer = " + nwell_layer + ";");
pw.println("$gds_contact_layer = " + contact_layer + ";");
pw.print("@gds_metal_layer = (");
for(int i=0; i<num_metal_layers; i++)
{
if (i > 0) pw.print(", ");
pw.print(metal_layers[i]);
}
pw.println(");");
pw.print("@gds_via_layer = (");
for(int i=0; i<num_metal_layers-1; i++)
{
if (i > 0) pw.print(", ");
pw.print(via_layers[i]);
}
pw.println(");");
pw.println();
pw.println("## Device marking layer");
pw.println("$gds_marking_layer = " + marking_layer + ";");
pw.println();
pw.println("# End of techfile");
}
/************************************** WRITE XML FILE **************************************/
void writeXML()
{
String errorMessage = errorInData();
if (errorMessage != null)
{
Job.getUserInterface().showErrorMessage("ERROR: " + errorMessage,
"Missing Technology Data");
return;
}
String suggestedName = getTechName() + ".xml";
String fileName = OpenFile.chooseOutputFile(FileType.XML, "Technology XML File", suggestedName); //"Technology.xml");
if (fileName == null) return;
try
{
dumpXMLFile(fileName);
} catch (Exception e)
{
System.out.println("Error writing XML file in '" + fileName + "'");
return;
}
}
/**
* Method to create the XML version of a PrimitiveNode representing a pin
* @return
*/
private Xml.PrimitiveNodeGroup makeXmlPrimitivePin(Xml.Technology t, String name, double size,
SizeOffset so, List<String> portNames, Xml.NodeLayer... list)
{
List<Xml.NodeLayer> nodesList = new ArrayList<Xml.NodeLayer>(list.length);
List<Xml.PrimitivePort> nodePorts = new ArrayList<Xml.PrimitivePort>();
for (Xml.NodeLayer lb : list)
{
if (lb == null) continue; // in case the pwell layer off
nodesList.add(lb);
}
// default uses the same name from the pin node
if (portNames == null)
{
portNames = new ArrayList<String>();
portNames.add(name);
}
nodePorts.add(makeXmlPrimitivePort(name.toLowerCase(), 0, 180, 0, null, 0, -1, 0, 1, 0, -1, 0, 1, portNames));
Xml.PrimitiveNodeGroup n = makeXmlPrimitive(t.nodeGroups, name + "-Pin", PrimitiveNode.Function.PIN, size, size, 0, 0,
so, nodesList, nodePorts, null, true);
return n;
}
/**
* Method to creat the XML version of a PrimitiveNode representing a contact
* @return
*/
private Xml.PrimitiveNodeGroup makeXmlPrimitiveCon(List<Xml.PrimitiveNodeGroup> nodeGroups, String name,
PrimitiveNode.Function function, double sizeX, double sizeY,
SizeOffset so, List<String> portArcNames, Xml.NodeLayer... list)
{
List<Xml.NodeLayer> nodesList = new ArrayList<Xml.NodeLayer>(list.length);
List<Xml.PrimitivePort> nodePorts = new ArrayList<Xml.PrimitivePort>();
for (Xml.NodeLayer lb : list)
{
if (lb == null) continue; // in case the pwell layer off
nodesList.add(lb);
}
nodePorts.add(makeXmlPrimitivePort(name.toLowerCase(), 0, 180, 0, null, 0, -1, 0, 1, 0, -1, 0, 1, portArcNames));
return makeXmlPrimitive(nodeGroups, name + "-Con", function, sizeX, sizeY, 0, 0,
so, nodesList, nodePorts, null, false);
}
/**
* Method to creat the XML version of a PrimitiveNode representing a contact
* @return
*/
private Xml.PrimitiveNodeGroup makeXmlCapacitor(List<Xml.PrimitiveNodeGroup> nodeGroups, String name,
PrimitiveNode.Function function, double sizeX, double sizeY,
SizeOffset so, List<String> portNames, List<String> portArcNames,
Xml.NodeLayer... list)
{
List<Xml.NodeLayer> nodesList = new ArrayList<Xml.NodeLayer>(list.length);
List<Xml.PrimitivePort> nodePorts = new ArrayList<Xml.PrimitivePort>();
for (Xml.NodeLayer lb : list)
{
if (lb == null) continue; // in case the pwell layer off
nodesList.add(lb);
}
for (String port : portNames)
{
nodePorts.add(makeXmlPrimitivePort(port, 0, 180, 0, null,
0, -1, 0, 1, 0, -1, 0, 1, portArcNames));
}
return makeXmlPrimitive(nodeGroups, name + "-Capacitor", function, sizeX, sizeY, 0, 0,
so, nodesList, nodePorts, null, false);
}
/**
* Method to create the XML version of a PrimitiveNode
* @return
*/
private Xml.PrimitiveNodeGroup makeXmlPrimitive(List<Xml.PrimitiveNodeGroup> nodeGroups,
String name, PrimitiveNode.Function function,
double width, double height,
double ppLeft, double ppBottom,
SizeOffset so, List<Xml.NodeLayer> nodeLayers,
List<Xml.PrimitivePort> nodePorts,
PrimitiveNode.NodeSizeRule nodeSizeRule, boolean isArcsShrink)
{
Xml.PrimitiveNodeGroup ng = new Xml.PrimitiveNodeGroup();
ng.isSingleton = true;
Xml.PrimitiveNode n = new Xml.PrimitiveNode();
n.name = name;
n.function = function;
ng.nodes.add(n);
ng.shrinkArcs = isArcsShrink;
// n.square = isSquare();
// n.canBeZeroSize = isCanBeZeroSize();
// n.wipes = isWipeOn1or2();
// n.lockable = isLockedPrim();
// n.edgeSelect = isEdgeSelect();
// n.skipSizeInPalette = isSkipSizeInPalette();
// n.notUsed = isNotUsed();
// n.lowVt = isNodeBitOn(PrimitiveNode.LOWVTBIT);
// n.highVt = isNodeBitOn(PrimitiveNode.HIGHVTBIT);
// n.nativeBit = isNodeBitOn(PrimitiveNode.NATIVEBIT);
// n.od18 = isNodeBitOn(PrimitiveNode.OD18BIT);
// n.od25 = isNodeBitOn(PrimitiveNode.OD25BIT);
// n.od33 = isNodeBitOn(PrimitiveNode.OD33BIT);
// PrimitiveNode.NodeSizeRule nodeSizeRule = getMinSizeRule();
// EPoint minFullSize = nodeSizeRule != null ?
// EPoint.fromLambda(0.5*nodeSizeRule.getWidth(), 0.5*nodeSizeRule.getHeight()) :
// EPoint.fromLambda(0.5*getDefWidth(), 0.5*getDefHeight());
// EPoint minFullSize = EPoint.fromLambda(0.5*width, 0.5*height);
EPoint topLeft = EPoint.fromLambda(ppLeft, ppBottom + height);
EPoint size = EPoint.fromLambda(width, height);
double getDefWidth = width, getDefHeight = height;
if (function.isPin() && isArcsShrink) {
// assert getNumPorts() == 1;
// assert nodeSizeRule == null;
// PrimitivePort pp = getPort(0);
// assert pp.getLeft().getMultiplier() == -0.5 && pp.getRight().getMultiplier() == 0.5 && pp.getBottom().getMultiplier() == -0.5 && pp.getTop().getMultiplier() == 0.5;
// assert pp.getLeft().getAdder() == -pp.getRight().getAdder() && pp.getBottom().getAdder() == -pp.getTop().getAdder();
// minFullSize = EPoint.fromLambda(ppLeft, ppBottom);
}
// DRCTemplate nodeSize = xmlRules.getRule(pnp.getPrimNodeIndexInTech(), DRCTemplate.DRCRuleType.NODSIZ);
// SizeOffset so = getProtoSizeOffset();
if (so != null &&
(so.getLowXOffset() == 0 && so.getHighXOffset() == 0 &&
so.getLowYOffset() == 0 && so.getHighYOffset() == 0))
so = null;
ERectangle base = calcBaseRectangle(so, nodeLayers, nodeSizeRule);
ng.baseLX.value = base.getLambdaMinX();
ng.baseHX.value = base.getLambdaMaxX();
ng.baseLY.value = base.getLambdaMinY();
ng.baseHY.value = base.getLambdaMaxY();
// n.sizeOffset = so;
// if (!minFullSize.equals(EPoint.ORIGIN))
// n.diskOffset = minFullSize;
// if (so != null) {
// EPoint p2 = EPoint.fromGrid(
// minFullSize.getGridX() - ((so.getLowXGridOffset() + so.getHighXGridOffset()) >> 1),
// minFullSize.getGridY() - ((so.getLowYGridOffset() + so.getHighYGridOffset()) >> 1));
// n.diskOffset.put(Integer.valueOf(1), minFullSize);
// n.diskOffset.put(Integer.valueOf(2), p2);
// n.diskOffset.put(Integer.valueOf(2), minFullSize);
// }
// n.defaultWidth.addLambda(DBMath.round(getDefWidth)); // - 2*minFullSize.getLambdaX());
// n.defaultHeight.addLambda(DBMath.round(getDefHeight)); // - 2*minFullSize.getLambdaY());
ERectangle baseRectangle = ERectangle.fromGrid(topLeft.getGridX(), topLeft.getGridY(),
size.getGridX(), size.getGridY());
/* n.nodeBase = baseRectangle;*/
// List<Technology.NodeLayer> nodeLayers = Arrays.asList(getLayers());
// List<Technology.NodeLayer> electricalNodeLayers = nodeLayers;
// if (getElectricalLayers() != null)
// electricalNodeLayers = Arrays.asList(getElectricalLayers());
boolean isSerp = false; //getSpecialType() == PrimitiveNode.SERPTRANS;
if (nodeLayers != null)
ng.nodeLayers.addAll(nodeLayers);
// int m = 0;
// for (Technology.NodeLayer nld: electricalNodeLayers) {
// int j = nodeLayers.indexOf(nld);
// if (j < 0) {
// n.nodeLayers.add(nld.makeXml(isSerp, minFullSize, false, true));
// continue;
// }
// while (m < j)
// n.nodeLayers.add(nodeLayers.get(m++).makeXml(isSerp, minFullSize, true, false));
// n.nodeLayers.add(nodeLayers.get(m++).makeXml(isSerp, minFullSize, true, true));
// }
// while (m < nodeLayers.size())
// n.nodeLayers.add(nodeLayers.get(m++).makeXml(isSerp, minFullSize, true, false));
// for (Iterator<PrimitivePort> pit = getPrimitivePorts(); pit.hasNext(); ) {
// PrimitivePort pp = pit.next();
// n.ports.add(pp.makeXml(minFullSize));
// }
ng.specialType = PrimitiveNode.NORMAL; // getSpecialType();
// if (getSpecialValues() != null)
// n.specialValues = getSpecialValues().clone();
if (nodeSizeRule != null) {
ng.nodeSizeRule = new Xml.NodeSizeRule();
ng.nodeSizeRule.width = nodeSizeRule.getWidth();
ng.nodeSizeRule.height = nodeSizeRule.getHeight();
ng.nodeSizeRule.rule = nodeSizeRule.getRuleName();
}
// n.spiceTemplate = "";//getSpiceTemplate();
// ports
ng.ports.addAll(nodePorts);
nodeGroups.add(ng);
return ng;
}
private ERectangle calcBaseRectangle(SizeOffset so, List<Xml.NodeLayer> nodeLayers, PrimitiveNode.NodeSizeRule nodeSizeRule) {
long lx, hx, ly, hy;
if (nodeSizeRule != null) {
hx = DBMath.lambdaToGrid(0.5*nodeSizeRule.getWidth());
lx = -hx;
hy = DBMath.lambdaToGrid(0.5*nodeSizeRule.getHeight());
ly = -hy;
} else {
lx = Long.MAX_VALUE;
hx = Long.MIN_VALUE;
ly = Long.MAX_VALUE;
hy = Long.MIN_VALUE;
for (int i = 0; i < nodeLayers.size(); i++) {
Xml.NodeLayer nl = nodeLayers.get(i);
long x, y;
if (nl.representation == Technology.NodeLayer.BOX || nl.representation == Technology.NodeLayer.MULTICUTBOX) {
x = DBMath.lambdaToGrid(nl.lx.value);
lx = Math.min(lx, x);
hx = Math.max(hx, x);
x = DBMath.lambdaToGrid(nl.hx.value);
lx = Math.min(lx, x);
hx = Math.max(hx, x);
y = DBMath.lambdaToGrid(nl.ly.value);
ly = Math.min(ly, y);
hy = Math.max(hy, y);
y = DBMath.lambdaToGrid(nl.hy.value);
ly = Math.min(ly, y);
hy = Math.max(hy, y);
} else {
for (Technology.TechPoint p: nl.techPoints) {
x = p.getX().getGridAdder();
lx = Math.min(lx, x);
hx = Math.max(hx, x);
y = p.getY().getGridAdder();
ly = Math.min(ly, y);
hy = Math.max(hy, y);
}
}
}
}
if (so != null) {
lx += so.getLowXGridOffset();
hx -= so.getHighXGridOffset();
ly += so.getLowYGridOffset();
hy -= so.getHighYGridOffset();
}
return ERectangle.fromGrid(lx, ly, hx - lx, hy - ly);
}
/**
* Method to create the XML version of a ArcProto
* @param name
* @param function
* @return
*/
private Xml.ArcProto makeXmlArc(Xml.Technology t, String name, com.sun.electric.technology.ArcProto.Function function,
double ant, Xml.ArcLayer ... arcLayers)
{
Xml.ArcProto a = new Xml.ArcProto();
a.name = name;
a.function = function;
a.wipable = true;
// a.curvable = false;
// a.special = false;
// a.notUsed = false;
// a.skipSizeInPalette = false;
// a.elibWidthOffset = getLambdaElibWidthOffset();
a.extended = true;
a.fixedAngle = true;
a.angleIncrement = 90;
a.antennaRatio = DBMath.round(ant);
for (Xml.ArcLayer al: arcLayers)
{
if (al == null) continue; // in case the pwell layer off
a.arcLayers.add(al);
}
t.arcs.add(a);
return a;
}
/**
* Method to create the XML version of a Layer.
* @return
*/
private Xml.Layer makeXmlLayer(List<Xml.Layer> layers, Map<Xml.Layer, WizardField> layerMap, String name,
Layer.Function function, int extraf, EGraphics graph,
WizardField width, boolean pureLayerNode, boolean pureLayerPortArc,
String... portArcNames)
{
Xml.Layer l = makeXmlLayer(layers, name, function, extraf, graph, width.value, pureLayerNode, pureLayerPortArc, portArcNames);
layerMap.put(l, width);
return l;
}
/**
* Method to create the XML version of a Layer.
* @return
*/
private Xml.Layer makeXmlLayer(List<Xml.Layer> layers, String name, Layer.Function function, int extraf, EGraphics graph,
double width, boolean pureLayerNode, boolean pureLayerPortArc, String... portArcNames)
{
Xml.Layer l = new Xml.Layer();
l.name = name;
l.function = function;
l.extraFunction = extraf;
graph = graph.withTransparencyMode(EGraphics.J3DTransparencyOption.NONE);
graph = graph.withTransparencyFactor(1);
l.desc = graph;
l.thick3D = 1;
l.height3D = 1;
l.cif = "Not set"; //"C" + cifLetter + cifLetter;
l.skill = name;
l.resistance = 1;
l.capacitance = 0;
l.edgeCapacitance = 0;
// if (layer.getPseudoLayer() != null)
// l.pseudoLayer = layer.getPseudoLayer().getName();
// if pureLayerNode is false, pureLayerPortArc must be false
assert(pureLayerNode || !pureLayerPortArc);
if (pureLayerNode) {
l.pureLayerNode = new Xml.PureLayerNode();
l.pureLayerNode.name = name + "-Node";
l.pureLayerNode.style = Poly.Type.FILLED;
l.pureLayerNode.size.addLambda(scaledValue(width));
l.pureLayerNode.port = "Port_" + name;
/* l.pureLayerNode.size.addRule(width.rule, 1);*/
if (pureLayerPortArc)
{
if (portArcNames.length == 0) // only 1 port
l.pureLayerNode.portArcs.add(name);
else
{
for (String s : portArcNames)
l.pureLayerNode.portArcs.add(s);
}
}
// for (ArcProto ap: pureLayerNode.getPort(0).getConnections()) {
// if (ap.getTechnology() != tech) continue;
// l.pureLayerNode.portArcs.add(ap.getName());
// }
}
layers.add(l);
return l;
}
private Xml.NodeLayer addXmlNodeLayer(List<Xml.NodeLayer> nodesList, Xml.Technology t, String layerName,
double xVal, double yVal)
{
return addXmlNodeLayerInternal(nodesList, t, layerName, xVal, yVal, true, true, 0);
}
private Xml.NodeLayer addXmlNodeLayerInternal(List<Xml.NodeLayer> nodesList, Xml.Technology t, String layerName,
double xVal, double yVal,
boolean inLayers, boolean electricalLayers, int port)
{
Xml.Layer layer = t.findLayer(layerName);
if (layer == null)
{
System.out.println("Error adding layer '" + layerName + "'");
return null;
}
Xml.NodeLayer nl = makeXmlNodeLayer(xVal, xVal, yVal, yVal, layer, Poly.Type.FILLED,
inLayers, electricalLayers, port);
nodesList.add(nl);
return nl;
}
/**
* Method to create the XML version of NodeLayer
*/
private Xml.NodeLayer makeXmlNodeLayer(double lx, double hx, double ly, double hy, Xml.Layer lb, Poly.Type style)
{
return makeXmlNodeLayer(lx, hx, ly, hy, lb, style, true, true, 0);
}
/**
* Method to create the XML version of NodeLayer either graphical or electrical.
* makeXmlNodeLayer is the default one where layer is available in both mode.
*/
private Xml.NodeLayer makeXmlNodeLayer(double lx, int lxk, double hx, int hxk,
double ly, int lyk, double hy, int hyk,
Xml.Layer lb, Poly.Type style,
boolean inLayers, boolean electricalLayers, int port)
{
Xml.NodeLayer nl = new Xml.NodeLayer();
// can't continue with rest of functions if this is null
// catching the exception in writeXML();
if(lb == null)
System.out.println("Error: null layer in makeXmlNodeLayer");
nl.layer = lb.name;
nl.style = style;
nl.portNum = port;
nl.inLayers = inLayers;
nl.inElectricalLayers = electricalLayers;
nl.representation = Technology.NodeLayer.BOX;
nl.lx.k = lxk; nl.hx.k = hxk; nl.ly.k = lyk; nl.hy.k = hyk;
nl.lx.addLambda(-lx); nl.hx.addLambda(hx); nl.ly.addLambda(-ly); nl.hy.addLambda(hy);
return nl;
}
/**
* Method to create the XML version of NodeLayer either graphical or electrical.
* makeXmlNodeLayer is the default one where layer is available in both mode.
*/
private Xml.NodeLayer makeXmlNodeLayer(double lx, double hx, double ly, double hy, Xml.Layer lb, Poly.Type style,
boolean inLayers, boolean electricalLayers, int port)
{
return makeXmlNodeLayer(lx, -1, hx, 1, ly, -1, hy, 1, lb, style, inLayers, electricalLayers, port);
}
/**
* Method to create the default XML version of a MultiCUt NodeLayer
* @return
*/
private Xml.NodeLayer makeXmlMulticut(Xml.Layer lb, double sizeRule, double sepRule, double sepRule2D)
{
return makeXmlMulticut(0, 0, 0, 0, lb, sizeRule, sepRule, sepRule2D);
}
/**
* Method to create the default XML version of a MultiCUt NodeLayer
* @return
*/
private Xml.NodeLayer makeXmlMulticut(double lx, double hx, double ly, double hy, Xml.Layer lb,
double sizeRule, double sepRule, double sepRule2D)
{
return makeXmlMulticut(lx, -1, hx, 1, ly, -1, hy, 1, lb, sizeRule, sepRule, sepRule2D);
}
/**
* Method to create the default XML version of a MultiCUt NodeLayer
* @return
*/
private Xml.NodeLayer makeXmlMulticut(double lx, int lxk, double hx, int hxk, double ly, int lyk, double hy, int hyk,
Xml.Layer lb, double sizeRule, double sepRule, double sepRule2D)
{
Xml.NodeLayer nl = new Xml.NodeLayer();
nl.layer = lb.name;
nl.style = Poly.Type.FILLED;
nl.inLayers = nl.inElectricalLayers = true;
nl.representation = Technology.NodeLayer.MULTICUTBOX;
nl.lx.k = lxk; nl.hx.k = hxk; nl.ly.k = lyk; nl.hy.k = hyk;
nl.lx.addLambda(-lx); nl.hx.addLambda(hx); nl.ly.addLambda(-ly); nl.hy.addLambda(hy);
// nl.sizeRule = sizeRule;
nl.sizex = sizeRule;
nl.sizey = sizeRule;
nl.sep1d = sepRule;
nl.sep2d = sepRule2D;
return nl;
}
/**
* Method to create the XML versio nof PrimitivePort
* @return New Xml.PrimitivePort
*/
private Xml.PrimitivePort makeXmlPrimitivePort(String name, int portAngle, int portRange, int portTopology,
EPoint minFullSize,
double lx, int slx, double hx, int shx,
double ly, int sly, double hy, int shy, List<String> portArcs)
{
Xml.PrimitivePort ppd = new Xml.PrimitivePort();
double lambdaX = (minFullSize != null) ? minFullSize.getLambdaX() : 0;
double lambdaY = (minFullSize != null) ? minFullSize.getLambdaY() : 0;
ppd.name = name;
ppd.portAngle = portAngle;
ppd.portRange = portRange;
ppd.portTopology = portTopology;
ppd.lx.k = slx;//-1; //getLeft().getMultiplier()*2;
ppd.lx.addLambda(DBMath.round(lx + lambdaX*ppd.lx.k));
ppd.hx.k = shx;//1; //getRight().getMultiplier()*2;
ppd.hx.addLambda(DBMath.round(hx + lambdaX*ppd.hx.k));
ppd.ly.k = sly;//-1; // getBottom().getMultiplier()*2;
ppd.ly.addLambda(DBMath.round(ly + lambdaY*ppd.ly.k));
ppd.hy.k = shy;//1; // getTop().getMultiplier()*2;
ppd.hy.addLambda(DBMath.round(hy + lambdaY*ppd.hy.k));
if (portArcs != null) {
for (String s: portArcs)
{
ppd.portArcs.add(s);
}
}
return ppd;
}
/**
* To create zero, cross, aligned and squared contacts from the same set of rules
*/
private Xml.PrimitiveNodeGroup makeContactSeries(List<Xml.PrimitiveNodeGroup> nodeGroups, String composeName,
double contSize, Xml.Layer conLayer, double spacing, double arraySpacing,
double extLayer1, Xml.Layer layer1,
double extLayer2, Xml.Layer layer2)
{
List<String> portNames = new ArrayList<String>(2);
portNames.add(layer1.name);
portNames.add(layer2.name);
// align contact
double hlaLong1 = DBMath.round(contSize/2 + extLayer1);
double hlaLong2 = DBMath.round(contSize/2 + extLayer2);
// double longD = DBMath.isGreaterThan(extLayer1, extLayer2) ? extLayer1 : extLayer2;
// long square contact. Standard ones
return (makeXmlPrimitiveCon(nodeGroups, composeName, PrimitiveNode.Function.CONTACT, -1, -1,
null, /*new SizeOffset(longD, longD, longD, longD),*/ portNames,
makeXmlNodeLayer(hlaLong1, hlaLong1, hlaLong1, hlaLong1, layer1, Poly.Type.FILLED), // layer1
makeXmlNodeLayer(hlaLong2, hlaLong2, hlaLong2, hlaLong2, layer2, Poly.Type.FILLED), // layer2
makeXmlMulticut(conLayer, contSize, spacing, arraySpacing))); // contact
}
/**
* Leave as oublic for the regression.
* @param fileName
* @throws IOException
*/
public void dumpXMLFile(String fileName)
throws IOException
{
Xml.Technology t = new Xml.Technology();
t.techName = getTechName();
t.shortTechName = getTechName();
t.description = getTechDescription();
t.minNumMetals = t.maxNumMetals = t.defaultNumMetals = getNumMetalLayers();
t.scaleValue = getStepSize();
t.scaleRelevant = true;
t.resolutionValue = getResolution();
// t.scaleRelevant = isScaleRelevant();
t.defaultFoundry = "NONE";
t.minResistance = 1.0;
t.minCapacitance = 0.1;
// menus
t.menuPalette = new Xml.MenuPalette();
t.menuPalette.numColumns = 3;
/** RULES **/
Xml.Foundry f = new Xml.Foundry();
f.name = Foundry.Type.NONE.getName();
t.foundries.add(f);
// LAYER COLOURS
Color [] metal_colour = new Color[]
{
new Color(0,150,255), // cyan/blue
new Color(148,0,211), // purple
new Color(255,215,0), // yellow
new Color(132,112,255), // mauve
new Color(255,160,122), // salmon
new Color(34,139,34), // dull green
new Color(178,34,34), // dull red
new Color(34,34,178), // dull blue
new Color(153,153,153), // light gray
new Color(102,102,102) // dark gray
};
Color poly_colour = new Color(255,155,192); // pink
Color diff_colour = new Color(107,226,96); // light green
Color via_colour = new Color(205,205,205); // lighter gray
// Five transparent colors: poly_colour, diff_colour, metal_colour[0->2]
Color[] colorMap = {poly_colour, diff_colour, metal_colour[0], metal_colour[1], metal_colour[2]};
for (int i = 0; i < colorMap.length; i++) {
Color transparentColor = colorMap[i];
t.transparentLayers.add(transparentColor);
}
// Layers
List<Xml.Layer> metalLayers = new ArrayList<Xml.Layer>();
List<Xml.Layer> dummyMetalLayers = new ArrayList<Xml.Layer>();
List<Xml.Layer> exclusionMetalLayers = new ArrayList<Xml.Layer>();
List<Xml.Layer> viaLayers = new ArrayList<Xml.Layer>();
Map<Xml.Layer,WizardField> layerMap = new LinkedHashMap<Xml.Layer,WizardField>();
int[] nullPattern = new int[] {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
int[] dexclPattern = new int[] {
0x1010, // X X
0x2020, // X X
0x4040, // X X
0x8080, // X X
0x4040, // X X
0x2020, // X X
0x1010, // X X
0x0808, // X X
0x1010, // X X
0x2020, // X X
0x4040, // X X
0x8080, // X X
0x4040, // X X
0x2020, // X X
0x1010, // X X
0x0808}; // X X
for (int i = 0; i < num_metal_layers; i++)
{
// Adding the metal
int metalNum = i + 1;
double opacity = (75 - metalNum * 5)/100.0;
int metLayHigh = i / 10;
int metLayDig = i % 10;
int r = metal_colour[metLayDig].getRed() * (10-metLayHigh) / 10;
int g = metal_colour[metLayDig].getGreen() * (10-metLayHigh) / 10;
int b = metal_colour[metLayDig].getBlue() * (10-metLayHigh) / 10;
int tcol = 0;
int[] pattern = null;
switch (metLayDig)
{
case 0: tcol = 3; break;
case 1: tcol = 4; break;
case 2: tcol = 5; break;
case 3: pattern = new int[] {0xFFFF, // XXXXXXXXXXXXXXXX
0x0000, //
0xFFFF, // XXXXXXXXXXXXXXXX
0x0000, //
0xFFFF, // XXXXXXXXXXXXXXXX
0x0000, //
0xFFFF, // XXXXXXXXXXXXXXXX
0x0000, //
0xFFFF, // XXXXXXXXXXXXXXXX
0x0000, //
0xFFFF, // XXXXXXXXXXXXXXXX
0x0000, //
0xFFFF, // XXXXXXXXXXXXXXXX
0x0000, //
0xFFFF, // XXXXXXXXXXXXXXXX
0x0000}; break;
case 4: pattern = new int[] { 0x8888, // X X X X
0x1111, // X X X X
0x2222, // X X X X
0x4444, // X X X X
0x8888, // X X X X
0x1111, // X X X X
0x2222, // X X X X
0x4444, // X X X X
0x8888, // X X X X
0x1111, // X X X X
0x2222, // X X X X
0x4444, // X X X X
0x8888, // X X X X
0x1111, // X X X X
0x2222, // X X X X
0x4444};
break;
case 5: pattern = new int[] { 0x1111, // X X X X
0xFFFF, // XXXXXXXXXXXXXXXX
0x1111, // X X X X
0x5555, // X X X X X X X X
0x1111, // X X X X
0xFFFF, // XXXXXXXXXXXXXXXX
0x1111, // X X X X
0x5555, // X X X X X X X X
0x1111, // X X X X
0xFFFF, // XXXXXXXXXXXXXXXX
0x1111, // X X X X
0x5555, // X X X X X X X X
0x1111, // X X X X
0xFFFF, // XXXXXXXXXXXXXXXX
0x1111, // X X X X
0x5555};
break;
case 6: pattern = new int[] { 0x8888, // X X X X
0x4444, // X X X X
0x2222, // X X X X
0x1111, // X X X X
0x8888, // X X X X
0x4444, // X X X X
0x2222, // X X X X
0x1111, // X X X X
0x8888, // X X X X
0x4444, // X X X X
0x2222, // X X X X
0x1111, // X X X X
0x8888, // X X X X
0x4444, // X X X X
0x2222, // X X X X
0x1111};
break;
case 7: pattern = new int[] { 0x2222, // X X X X
0x0000, //
0x8888, // X X X X
0x0000, //
0x2222, // X X X X
0x0000, //
0x8888, // X X X X
0x0000, //
0x2222, // X X X X
0x0000, //
0x8888, // X X X X
0x0000, //
0x2222, // X X X X
0x0000, //
0x8888, // X X X X
0x0000};
break;
case 8: pattern = new int[] {0x0000, //
0x2222, // X X X X
0x0000, //
0x8888, // X X X X
0x0000, //
0x2222, // X X X X
0x0000, //
0x8888, // X X X X
0x0000, //
0x2222, // X X X X
0x0000, //
0x8888, // X X X X
0x0000, //
0x2222, // X X X X
0x0000, //
0x8888}; // X X X X
break;
case 9: pattern = new int[] { 0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555, // X X X X X X X X
0x5555};
break;
}
boolean onDisplay = true, onPrinter = true;
if (pattern == null)
{
pattern = nullPattern;
onDisplay = false; onPrinter = false;
}
EGraphics graph = new EGraphics(onDisplay, onPrinter, null, tcol, r, g, b, opacity, true, pattern);
Layer.Function fun = Layer.Function.getMetal(metalNum);
if (fun == null)
throw new IOException("invalid number of metals");
String metalName = "Metal-"+metalNum;
Xml.Layer layer = makeXmlLayer(t.layers, layerMap, metalName, fun, 0, graph,
metal_width[i], true, true);
metalLayers.add(layer);
if (isComplexCase())
{
// dummy layers
graph = new EGraphics(true, true, null, tcol, r, g, b, opacity, false, nullPattern);
layer = makeXmlLayer(t.layers, "DMY-"+metalName, Layer.Function.getDummyMetal(metalNum), 0, graph,
5*metal_width[i].value, true, false);
dummyMetalLayers.add(layer);
// exclusion layers for metals
graph = new EGraphics(true, true, null, tcol, r, g, b, opacity, true, dexclPattern);
layer = makeXmlLayer(t.layers, "DEXCL-"+metalName, Layer.Function.getDummyExclMetal(i), 0, graph,
2*metal_width[i].value, true, false);
exclusionMetalLayers.add(layer);
}
}
// Vias
for (int i = 0; i < num_metal_layers - 1; i++)
{
// Adding the metal
int metalNum = i + 1;
// adding the via
int r = via_colour.getRed();
int g = via_colour.getGreen();
int b = via_colour.getBlue();
double opacity = 0.7;
EGraphics graph = new EGraphics(false, false, null, 0, r, g, b, opacity, true, nullPattern);
Layer.Function fun = Layer.Function.getContact(metalNum+1); //via contact starts with CONTACT2
if (fun == null)
throw new IOException("invalid number of vias");
viaLayers.add(makeXmlLayer(t.layers, layerMap, "Via-"+metalNum, fun, Layer.Function.CONMETAL,
graph, via_size[i], true, false));
}
// Aggregating all palette groups into one
List<PaletteGroup> allGroups = new ArrayList<PaletteGroup>();
EGraphics graph;
if (!isBasicCase())
addStandardLayers(t, layerMap, nullPattern, dexclPattern);
if (isComplexCase())
{
// exclusion layer N/P diff
graph = new EGraphics(true, true, null, 2, 0, 0, 0, 1, true, dexclPattern);
Xml.Layer exclusionDiffPLayer = makeXmlLayer(t.layers, "DEXCL-P-"+ diff_layer.name, Layer.Function.DEXCLDIFF, 0, graph,
2*diff_width.value, true, false);
Xml.Layer exclusionDiffNLayer = makeXmlLayer(t.layers, "DEXCL-N-"+ diff_layer.name, Layer.Function.DEXCLDIFF, 0, graph,
2*diff_width.value, true, false);
makeLayerGDS(t, exclusionDiffPLayer, "150/20");
makeLayerGDS(t, exclusionDiffNLayer, "150/20");
}
// DeviceMark
graph = new EGraphics(false, false, null, 0, 255, 0, 0, 0.4, true, nullPattern);
Xml.Layer deviceMarkLayer = makeXmlLayer(t.layers, layerMap, marking_layer.name, Layer.Function.CONTROL, 0,
graph, nplus_width, true, false);
// Extra layers
List<PaletteGroup> extraPaletteList = new ArrayList<PaletteGroup>();
for (LayerInfo info : extraLayers)
{
graph = null;
// either color or template
assert (info.graphicsTemplate == null || info.graphicsColor == null);
if (info.graphicsTemplate != null)
{
// look for layer name and get its EGraphics
for (Xml.Layer l : t.layers)
{
if (l.name.equals(info.graphicsTemplate))
{
graph = l.desc;
break;
}
}
if (graph == null)
System.out.println("No template layer " + info.graphicsTemplate + " found");
}
else if (info.graphicsColor != null)
{
boolean displayPatterned = (info.graphicsOutline != EGraphics.Outline.NOPAT);
graph = new EGraphics(displayPatterned, displayPatterned, info.graphicsOutline, 0,
info.graphicsColor.getRed(), info.graphicsColor.getGreen(), info.graphicsColor.getBlue(),
1, true, info.graphicsPattern);
}
if (graph == null)
graph = new EGraphics(false, false, null, 0, 255, 0, 0, 0.4, true, nullPattern);
if (DBMath.areEquals(info.width, 0))
System.out.println("Adding pure layer node '" + info.name + "' with zero width");
WizardField wf = new WizardField(info.width, info.name); // name is irrelevant
Xml.Layer layer = makeXmlLayer(t.layers, layerMap, info.name, info.function, 0, graph,
wf, true, info.addArc, info.name);
if (info.cif != null) layer.cif = info.cif;
makeLayerGDS(t, layer, String.valueOf(info));
if (info.addArc)
{
info.grp = new PaletteGroup();
info.grp.addArc(makeXmlArc(t, info.name, ArcProto.Function.UNKNOWN, 0,
makeXmlArcLayer(layer, wf)));
double hla = scaledValue(info.width / 2);
info.grp.addPinOrResistor(makeXmlPrimitivePin(t, info.name, hla, null,
null, makeXmlNodeLayer(hla, hla, hla, hla, layer, Poly.Type.CROSSED)), null);
extraPaletteList.add(info.grp);
}
// Adding 3D info
if (info.height > -1) // -1 is the default valuu
layer.height3D = info.height;
if (info.thickness > -1) // -1 is the default valuu
layer.thick3D = info.thickness;
}
// Generic contacts which might be based on extraLayers
addGenericContacts(t, noMultiContacts, extraPaletteList);
// Palette elements should be added at the end so they will appear in groups
PaletteGroup[] metalPalette = new PaletteGroup[num_metal_layers];
/**************************** Metal Nodes/Arcs ***********************************************/
// write metal arcs
for(int i=1; i<=num_metal_layers; i++)
{
double ant = (int)Math.round(metal_antenna_ratio[i-1]) | 200;
PaletteGroup group = new PaletteGroup();
metalPalette[i-1] = group;
group.addArc(makeXmlArc(t, "Metal-"+i, ArcProto.Function.getContact(i), ant,
makeXmlArcLayer(metalLayers.get(i-1), metal_width[i-1])));
}
if (!isBasicCase())
addStandardElements(t, layerMap, metalLayers, allGroups);
/**************************** Metals Nodes/Arcs ***********************************************/
// Pins
for (int i = 0; i < num_metal_layers; i++)
{
double hla = scaledValue(metal_width[i].value / 2);
Xml.Layer lt = metalLayers.get(i);
PaletteGroup group = metalPalette[i]; // structure created by the arc definition
group.addPinOrResistor(makeXmlPrimitivePin(t, lt.name, hla, null, //new SizeOffset(hla, hla, hla, hla),
null, makeXmlNodeLayer(hla, hla, hla, hla, lt, Poly.Type.CROSSED)), null);
}
// contacts
for(int i=1; i<num_metal_layers; i++)
{
Xml.Layer lb = metalLayers.get(i-1);
Xml.Layer lt = metalLayers.get(i);
PaletteGroup group = metalPalette[i-1]; // structure created by the arc definition
if (!isComplexCase())
{
// original contact Square
// via
Xml.Layer via = viaLayers.get(i-1);
double viaSize = scaledValue(via_size[i-1].value);
double viaSpacing = scaledValue(via_inline_spacing[i-1].value);
double viaArraySpacing = scaledValue(via_array_spacing[i-1].value);
String name = lb.name + "-" + lt.name;
double longDist = scaledValue(via_overhang[i-1].value);
group.addElement(makeContactSeries(t.nodeGroups, name, viaSize, via, viaSpacing, viaArraySpacing,
longDist, lt, longDist, lb), null);
}
}
List<String> portNames = new ArrayList<String>();
// metal contacts
for (Map.Entry<String,List<Contact>> e : metalContacts.entrySet())
{
// generic contacts
for (Contact c : e.getValue())
{
// We know those layer names are numbers!
assert(c.layers.size() == 2);
RectLayerNode verticalLayer = (RectLayerNode)c.layers.get(0);
RectLayerNode horizontalLayer = (RectLayerNode)c.layers.get(1);
int i = Integer.valueOf(verticalLayer.layer);
int j = Integer.valueOf(horizontalLayer.layer);
Xml.Layer ly = metalLayers.get(i-1);
Xml.Layer lx = metalLayers.get(j-1);
String name = (j>i)?ly.name + "-" + lx.name:lx.name + "-" + ly.name;
int via = (j>i)?i:j;
double metalContSize = scaledValue(via_size[via-1].value);
double spacing = scaledValue(via_inline_spacing[via-1].value);
double arraySpacing = scaledValue(via_array_spacing[via-1].value);
Xml.Layer metalConLayer = viaLayers.get(via-1);
double h1x = scaledValue(via_size[via-1].value /2 + verticalLayer.valueX.value);
double h1y = scaledValue(via_size[via-1].value /2 + verticalLayer.valueY.value);
double h2x = scaledValue(via_size[via-1].value /2 + horizontalLayer.valueX.value);
double h2y = scaledValue(via_size[via-1].value /2 + horizontalLayer.valueY.value);
double longX = scaledValue(Math.abs(verticalLayer.valueX.value - horizontalLayer.valueX.value));
double longY = scaledValue(Math.abs(verticalLayer.valueY.value - horizontalLayer.valueY.value));
portNames.clear();
portNames.add(lx.name);
portNames.add(ly.name);
// some primitives might not have prefix. "-" should not be in the prefix to avoid
// being displayed in the palette
String p = (c.prefix == null || c.prefix.equals("")) ? "" : c.prefix + "-";
metalPalette[via-1].addElement(makeXmlPrimitiveCon(t.nodeGroups, p + name, PrimitiveNode.Function.CONTACT, -1, -1,
new SizeOffset(longX, longX, longY, longY),
portNames,
makeXmlNodeLayer(h1x, h1x, h1y, h1y, ly, Poly.Type.FILLED), // layer1
makeXmlNodeLayer(h2x, h2x, h2y, h2y, lx, Poly.Type.FILLED), // layer2
makeXmlMulticut(metalConLayer, metalContSize, spacing, arraySpacing)), c.prefix); // contact
}
}
// Aggregating all palette groups into one
for (PaletteGroup g : metalPalette)
allGroups.add(g);
// Extra layers with pins/arcs
allGroups.addAll(extraPaletteList);
// Adding elements in palette
for (PaletteGroup o : allGroups)
{
t.menuPalette.menuBoxes.add(o.arcs); // arcs
t.menuPalette.menuBoxes.add(o.pins); // pins
t.menuPalette.menuBoxes.add(o.elements); // contacts
}
// Writting GDS values
makeLayerGDS(t, deviceMarkLayer, String.valueOf(marking_layer));
for (int i = 0; i < num_metal_layers; i++) {
Xml.Layer met = metalLayers.get(i);
makeLayerGDS(t, met, String.valueOf(metal_layers[i]));
if (isComplexCase())
{
// Type is always 1
makeLayerGDS(t, dummyMetalLayers.get(i), metal_layers[i].value + "/1");
// exclusion always takes 150
makeLayerGDS(t, exclusionMetalLayers.get(i), "150/" + (i + 1));
}
if (i > num_metal_layers - 2) continue;
Xml.Layer via = viaLayers.get(i);
makeLayerGDS(t, via, String.valueOf(via_layers[i]));
}
//
// Writting Layer Rules
//
// Simple spacing rules included here
for (int i = 0; i < num_metal_layers; i++) {
Xml.Layer met = metalLayers.get(i);
makeLayerRuleMinRule(t, met, DRCTemplate.DRCRuleType.MINWID, metal_width[i]);
makeLayerRuleMinRule(t, met, DRCTemplate.DRCRuleType.MINAREA, metal_minarea[i]);
makeLayerRuleMinRule(t, met, DRCTemplate.DRCRuleType.MINENCLOSEDAREA, metal_enclosedarea[i]);
makeLayersRule(t, met, DRCTemplate.DRCRuleType.SPACING, metal_spacing[i].rule, metal_spacing[i].value);
if (i >= num_metal_layers - 1) continue;
Xml.Layer via = viaLayers.get(i);
makeLayerRuleMinRule(t, via, DRCTemplate.DRCRuleType.MINWID, via_size[i]);
makeLayersRule(t, via, DRCTemplate.DRCRuleType.SPACING, via_inline_spacing[i].rule, via_inline_spacing[i].value);
}
// wide metal rules
for (WideWizardField w : wide_metal_spacing)
{
for (String layerName : w.names)
{
Xml.Layer layer = t.findLayer(layerName);
assert(layer != null);
makeLayersWideRule(t, layer, DRCTemplate.DRCRuleType.SPACING, w.rule, w.value, w.maxW, w.minLen);
}
}
// spacing/min rules in extra layers
for (LayerInfo layer : extraLayers)
{
Xml.Layer l = t.findLayer(layer.name);
if (layer.minimum != null)
makeLayerRuleMinRule(t, l, DRCTemplate.DRCRuleType.MINWID, layer.minimum);
if (layer.spacing != null)
makeLayersRule(t, l, DRCTemplate.DRCRuleType.SPACING, layer.spacing.rule, layer.spacing.value);
}
// Finish menu with Pure, Misc and Cell
List<Object> l = new ArrayList<Object>();
l.add(new String("Pure"));
t.menuPalette.menuBoxes.add(l);
l = new ArrayList<Object>();
l.add(new String("Misc."));
t.menuPalette.menuBoxes.add(l);
l = new ArrayList<Object>();
l.add(new String("Cell"));
t.menuPalette.menuBoxes.add(l);
// Sort before writing data. We might need to sort primitive nodes in group before...
Collections.sort(t.nodeGroups, primitiveNodeGroupSort);
for (Xml.PrimitiveNodeGroup nodeGroup: t.nodeGroups)
{
// sort NodeLayer before writing them
Collections.sort(nodeGroup.nodeLayers, nodeLayerSort);
}
// write finally the file
boolean includeDateAndVersion = User.isIncludeDateAndVersionInOutput();
String copyrightMessage = IOTool.isUseCopyrightMessage() ? IOTool.getCopyrightMessage() : null;
t.writeXml(fileName, includeDateAndVersion, copyrightMessage);
}
private PrimitiveNode.Function getWellContactFunction(int i)
{
if (i == Technology.P_TYPE)
return (pSubstrateProcess) ? PrimitiveNode.Function.SUBSTRATE : PrimitiveNode.Function.WELL;
return (pSubstrateProcess) ? PrimitiveNode.Function.WELL : PrimitiveNode.Function.SUBSTRATE;
}
private void prepareTransistor(double gateWidth, double gateLength, double polyEndcap, double diffPolyOverhang,
double gateContactSpacing, double contactSize,
Xml.Layer activeLayer, Xml.Layer polyLayer, Xml.Layer polyGateLayer,
List<Xml.NodeLayer> nodesList, List<Xml.PrimitivePort> nodePorts)
{
double impx = scaledValue((gateWidth)/2);
double impy = scaledValue((gateLength+diffPolyOverhang*2)/2);
double diffY = scaledValue(gateLength/2+gateContactSpacing+contactSize/2); // impy
double diffX = 0;
double xSign = 1, ySign = -1;
// Active layers
nodesList.add(makeXmlNodeLayer(impx, impx, impy, impy, activeLayer, Poly.Type.FILLED, true, false, -1));
// electrical active layers
nodesList.add(makeXmlNodeLayer(impx, impx, impy, 0, activeLayer, Poly.Type.FILLED, false, true, 3)); // bottom
nodesList.add(makeXmlNodeLayer(impx, impx, 0, impy, activeLayer, Poly.Type.FILLED, false, true, 1)); // top
// Diff port
List<String> portNames = new ArrayList<String>();
portNames.add(activeLayer.name);
// top port
Xml.PrimitivePort diffTopPort = makeXmlPrimitivePort("diff-top", 90, 90, 1, null,
diffX, -1, diffX, 1, diffY, 1, diffY, 1, portNames);
// bottom port
Xml.PrimitivePort diffBottomPort = makeXmlPrimitivePort("diff-bottom", 270, 90, 2, null,
xSign*diffX, -1, xSign*diffX, 1, ySign*diffY, -1, ySign*diffY, -1, portNames);
// Electric layers
// Gate layer Electrical
double gatey = scaledValue(gateLength/2);
double gatex = impx;
double endPolyx = scaledValue((gateWidth+polyEndcap*2)/2);
double endPolyy = gatey;
double endLeftOrRight = -impx;
double endTopOrBotton = endPolyy;
double polyX = endPolyx;
double polyY = 0;
nodesList.add(makeXmlNodeLayer(gatex, gatex, gatey, gatey, polyGateLayer, Poly.Type.FILLED, false, true, -1));
// Poly layers
// left electrical
nodesList.add(makeXmlNodeLayer(endPolyx, endLeftOrRight, endPolyy, endTopOrBotton, polyLayer,
Poly.Type.FILLED, false, true, 0));
// right electrical
nodesList.add(makeXmlNodeLayer(endLeftOrRight, endPolyx, endTopOrBotton, endPolyy, polyLayer,
Poly.Type.FILLED, false, true, 2));
// non-electrical poly (just one poly layer)
nodesList.add(makeXmlNodeLayer(endPolyx, endPolyx, endPolyy, endPolyy, polyLayer, Poly.Type.FILLED, true, false, -1));
// Poly port
portNames.clear();
portNames.add(polyLayer.name);
Xml.PrimitivePort polyLeftPort = makeXmlPrimitivePort("poly-left", 180, 90, 0, null,
ySign*polyX, -1, ySign*polyX,
-1, xSign*polyY, -1, xSign*polyY, 1, portNames);
// right port
Xml.PrimitivePort polyRightPort = makeXmlPrimitivePort("poly-right", 0, 180, 0, null,
polyX, 1, polyX, 1, polyY, -1, polyY, 1, portNames);
nodePorts.clear();
nodePorts.add(polyLeftPort);
nodePorts.add(diffTopPort);
nodePorts.add(polyRightPort);
nodePorts.add(diffBottomPort);
}
private Xml.ArcLayer makeXmlArcLayer(Xml.Layer layer, WizardField ... flds) {
Xml.ArcLayer al = new Xml.ArcLayer();
al.layer = layer.name;
al.style = Poly.Type.FILLED;
for (int i = 0; i < flds.length; i++)
al.extend.addLambda(scaledValue(flds[i].value /2));
return al;
}
// private Technology.Distance makeXmlDistance(WizardField ... flds) {
// Technology.Distance dist = new Technology.Distance();
// dist.addRule(flds[0].rule, 0.5);
// for (int i = 1; i < flds.length; i++)
// dist.addRule(flds[i].rule, 1);
// return dist;
// }
private void makeLayerGDS(Xml.Technology t, Xml.Layer l, String gdsVal) {
for (Xml.Foundry f: t.foundries) {
f.layerGds.put(l.name, gdsVal);
}
}
private void makeLayerRuleMinRule(Xml.Technology t, Xml.Layer l, DRCTemplate.DRCRuleType type, WizardField fld)
{
// not elegant way to detect valid value but it should work
if (fld.value == 0.0)
return; // nothing to add
for (Xml.Foundry f: t.foundries) {
f.rules.add(new DRCTemplate(fld.rule, DRCTemplate.DRCMode.ALL.mode(), type,
l.name, null, new double[] {scaledValue(fld.value)}, null, null));
}
}
private void makeLayersWideRule(Xml.Technology t, Xml.Layer l, DRCTemplate.DRCRuleType ruleType, String ruleName,
double ruleValue, double maxW, double minLen) {
for (Xml.Foundry f: t.foundries) {
f.rules.add(new DRCTemplate(ruleName, DRCTemplate.DRCMode.ALL.mode(), ruleType, maxW, minLen,
l.name, l.name, new double[] {scaledValue(ruleValue)}, -1));
}
}
private void makeLayersRule(Xml.Technology t, Xml.Layer l, DRCTemplate.DRCRuleType ruleType, String ruleName, double ruleValue) {
for (Xml.Foundry f: t.foundries) {
f.rules.add(new DRCTemplate(ruleName, DRCTemplate.DRCMode.ALL.mode(), ruleType,
l.name, l.name, new double[] {scaledValue(ruleValue)}, null, null));
}
}
private void makeLayersRuleSpacing(Xml.Technology t, Xml.Layer l1, Xml.Layer l2, String ruleName, double ruleValue) {
double value = scaledValue(ruleValue);
for (Xml.Foundry f: t.foundries) {
f.rules.add(new DRCTemplate(ruleName, DRCTemplate.DRCMode.ALL.mode(), DRCTemplate.DRCRuleType.SPACING,
l1.name, l2.name, new double[] {value, value}, null, null));
}
}
private void makeLayersRuleSurround(Xml.Technology t, Xml.Layer l1, Xml.Layer l2, String ruleName, double ruleValue) {
double value = scaledValue(ruleValue);
for (Xml.Foundry f: t.foundries) {
f.rules.add(new DRCTemplate(ruleName, DRCTemplate.DRCMode.ALL.mode(), DRCTemplate.DRCRuleType.SURROUND,
l1.name, l2.name, new double[] {value, value}, null, null));
}
}
private double scaledValue(double val) { return DBMath.round(val / stepsize); }
/***************************************************************************************************
* Analog Elements
***************************************************************************************************/
private void createSecondPolyElements(Xml.Technology t, Map<Xml.Layer,WizardField> layerMap,
List<PaletteGroup> polysGroup)
{
int[] nullPattern = new int[] {44975, 34952, 64250, 34952, 44975, 34952, 64250, 34952,
44975, 34952, 64250, 34952, 44975, 34952, 64250, 34952};
EGraphics graph = new EGraphics(true, true, null, 0, 255, 190, 6, 1, true, nullPattern);
Xml.Layer poly2Layer = makeXmlLayer(t.layers, layerMap, poly2_layer.name, Layer.Function.POLY2, 0, graph,
poly_width, true, true);
PaletteGroup poly2Group = new PaletteGroup();
double ant = (int)Math.round(poly_antenna_ratio) | 200;
// Arc
poly2Group.addArc(makeXmlArc(t, poly2Layer.name, ArcProto.Function.getPoly(2), ant,
makeXmlArcLayer(poly2Layer, poly_width)));
polysGroup.add(poly2Group);
// pin
double hla = scaledValue(poly_width.value / 2);
poly2Group.addPinOrResistor(makeXmlPrimitivePin(t, poly2Layer.name, hla, null, // new SizeOffset(hla, hla, hla, hla),
null, makeXmlNodeLayer(hla, hla, hla, hla, poly2Layer, Poly.Type.CROSSED)), null);
}
private void createAnalogElements(Xml.Technology t, List<Xml.Layer> metalLayers, List<PaletteGroup> polysGroup)
{
List<Xml.NodeLayer> nodesList = new ArrayList<Xml.NodeLayer>();
List<Xml.PrimitivePort> nodePorts = new ArrayList<Xml.PrimitivePort>();
Xml.Layer poly2Layer = t.findLayer(poly2_layer.name);
assert(poly2Layer != null);
Xml.Layer hiRestLayer = t.findLayer("Hi-Res");
Xml.Layer polyConLayer = t.findLayer(poly_layer.name+"-Cut");
PaletteGroup g = polysGroup.get(1); // second group in polys
/*************************************/
// Analog Capacitors
/*************************************/
for (Map.Entry<String,List<Contact>> e : capacitors.entrySet())
{
addContactsOrCapacitors(t, e.getValue(), metalLayers, null, null, g, true);
}
/*************************************/
// Analog Hi Poly Resistors
/*************************************/
WizardField polyRL = findWizardField("hi_poly_resistor_length");
WizardField poly2Overhang = findWizardField("contact_poly2_overhang");
WizardField hiRestOverhang = findWizardField("hi-res_overhang");
// using array value to guarantee proper spacing in nD cases
addMetalElements(t, polyConLayer, contact_array_spacing.value, polyRL, poly2Overhang, nodesList, nodePorts);
// poly
double polyNoScaled = 2 * (poly2Overhang.value) + contact_size.value;
double soxNoScaled = /*(hiRestOverhang.value) + */polyNoScaled;
double polyL = scaledValue(polyRL.value /2 + polyNoScaled);
double polyWNoScaled = scaledValue(contact_size.value/2 + poly2Overhang.value);
double polyW = scaledValue(polyWNoScaled);
nodesList.add(makeXmlNodeLayer(polyL, polyL, polyW, polyW, poly2Layer,
Poly.Type.FILLED, true, true, 0));
// hi res
double hiresL = scaledValue(polyRL.value /2 + hiRestOverhang.value); // soxNoScaled);
double hiresW = scaledValue(polyWNoScaled + hiRestOverhang.value);
nodesList.add(makeXmlNodeLayer(hiresL, hiresL, hiresW, hiresW, hiRestLayer,
Poly.Type.FILLED, true, true, 0));
double sox = scaledValue(soxNoScaled);
double soy = scaledValue(hiRestOverhang.value);
Xml.PrimitiveNodeGroup n = makeXmlPrimitive(t.nodeGroups, "Hi-Res-Poly2-Resistor",
PrimitiveNode.Function.RESHIRESPOLY2, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy),
nodesList, nodePorts, null, false);
g.addElement(n, "Hi-RPoly2");
/*************************************/
// Analog Active Resistors
/*************************************/
WizardField activeRL = findWizardField("active_resistor_length"); //
String[] diffNames = {"P", "N"};
Xml.Layer activeConLayer = t.findLayer(diff_layer.name+"-Cut");
for (int i = 0; i < 2; i++)
{
// active resistors
Xml.Layer activeLayer = t.findLayer(diffNames[i]+"-"+diff_layer.name); //$nplus_overhang_diff
Xml.Layer selectLayer, wellLayer;
WizardField selectWF;
PrimitiveNode.Function func;
nodesList.clear();
nodePorts.clear();
if (i==Technology.P_TYPE)
{
selectLayer = t.findLayer(pplus_layer.name);
selectWF = pplus_overhang_diff;
wellLayer = t.findLayer(nwell_layer.name);
func = PrimitiveNode.Function.RESPACTIVE;
}
else
{
selectLayer = t.findLayer(nplus_layer.name);
selectWF = nplus_overhang_diff;
wellLayer = t.findLayer("P-Well");
func = PrimitiveNode.Function.RESNACTIVE;
}
// active layer
double activeNoScaled = 2 * (diff_contact_overhang.value) + contact_size.value;
double activeL = scaledValue(activeRL.value /2 + activeNoScaled);
double activeWNoScaled = contact_size.value/2 + diff_contact_overhang.value;
double activeW = scaledValue(activeWNoScaled);
nodesList.add(makeXmlNodeLayer(activeL, activeL, activeW, activeW, activeLayer, Poly.Type.FILLED, true, true, 0));
// select layer
double selectOverhang = scaledValue(selectWF.value);
double selectL = selectOverhang + activeL;
double selectW = selectOverhang + activeW;
nodesList.add(makeXmlNodeLayer(selectL, selectL, selectW, selectW, selectLayer, Poly.Type.FILLED, true, true, 0));
// well layer
Xml.NodeLayer wellNodeLayer = null;
if (!getPSubstratelProcess())
{
double wellOverhang = scaledValue(nwell_overhang_diff_p.value - selectWF.value);
double wellL = wellOverhang + selectL;
double wellW = wellOverhang + selectW;
wellNodeLayer = makeXmlNodeLayer(wellL, wellL, wellW, wellW, wellLayer, Poly.Type.FILLED, true, true, 0);
nodesList.add(wellNodeLayer);
}
addMetalElements(t, activeConLayer, contact_array_spacing.value, activeRL, diff_contact_overhang, nodesList, nodePorts);
sox = scaledValue(nwell_overhang_diff_p.value + activeNoScaled);
soy = scaledValue(nwell_overhang_diff_p.value);
n = makeXmlPrimitive(t.nodeGroups, diffNames[i]+"-Active-Resistor", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy),
nodesList, nodePorts, null, false);
g.addElement(n, diffNames[i]+"-RActive");
}
/*************************************/
// Analog Well Resistors
/*************************************/
WizardField wellRL = findWizardField("well_resistor_length");
for (int i = 0; i < 2; i++)
{
Xml.Layer activeLayer = t.findLayer(diffNames[i]+"-"+diff_layer.name); //$nplus_overhang_diff
Xml.Layer selectLayer, wellLayer;
WizardField selectWF;
PrimitiveNode.Function func;
nodesList.clear();
nodePorts.clear();
if (i==Technology.P_TYPE)
{
selectLayer = t.findLayer(pplus_layer.name);
selectWF = pplus_overhang_diff;
wellLayer = t.findLayer("P-Well");
func = PrimitiveNode.Function.RESPWELL;
}
else
{
selectLayer = t.findLayer(nplus_layer.name);
selectWF = nplus_overhang_diff;
wellLayer = t.findLayer(nwell_layer.name);
func = PrimitiveNode.Function.RESNWELL;
}
// active layer
double activeNoScaled = 2 * (diff_contact_overhang.value) + contact_size.value;
double activeDistance = scaledValue(wellRL.value /2);
double activeX = scaledValue(2 * (diff_contact_overhang.value) + contact_size.value);
double activeL = scaledValue(wellRL.value /2 + activeNoScaled);
double activeWNoScaled = contact_size.value/2 + diff_contact_overhang.value;
double activeW = scaledValue(activeWNoScaled);
nodesList.add(makeXmlNodeLayer((activeDistance + activeX), -1, -activeDistance, -1, activeW, -1, activeW, 1, activeLayer,
Poly.Type.FILLED, true, true, 0));
// right metal
nodesList.add(makeXmlNodeLayer(-activeDistance, 1, (activeDistance + activeX), 1, activeW, -1, activeW, 1, activeLayer,
Poly.Type.FILLED, true, true, 1));
// select layer
double selectOverhang = scaledValue(selectWF.value);
double selectL = selectOverhang + activeL;
double selectW = selectOverhang + activeW;
double selectDistance = activeDistance - scaledValue(selectWF.value);
double selectX = activeX + 2 * (selectWF.value);
nodesList.add(makeXmlNodeLayer((selectDistance + selectX), -1, -selectDistance, -1, selectW, -1, selectW, 1, selectLayer,
Poly.Type.FILLED, true, true, 0));
// right metal
nodesList.add(makeXmlNodeLayer(-selectDistance, 1, (selectDistance + selectX), 1, selectW, -1, selectW, 1, selectLayer,
Poly.Type.FILLED, true, true, 1));
// well layer
Xml.NodeLayer wellNodeLayer = null;
if (!getPSubstratelProcess())
{
double wellOverhang = scaledValue(nwell_overhang_diff_n.value - selectWF.value);
double wellL = wellOverhang + selectL;
double wellW = wellOverhang + selectW;
wellNodeLayer = makeXmlNodeLayer(wellL, wellL, wellW, wellW, wellLayer, Poly.Type.FILLED, true, true, 0);
nodesList.add(wellNodeLayer);
}
addMetalElements(t, activeConLayer, contact_array_spacing.value, wellRL, diff_contact_overhang, nodesList, nodePorts);
sox = scaledValue(nwell_overhang_diff_n.value) + activeX + selectOverhang;
soy = 0; // scaledValue(nwell_overhang_diff_n.value);
n = makeXmlPrimitive(t.nodeGroups, diffNames[i]+"-Well-Resistor", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy),
nodesList, nodePorts, null, false);
g.addElement(n, diffNames[i]+"-RWell");
}
/*************************************/
// Analog Poly Resistors (no hi res)
/*************************************/
polyRL = findWizardField("poly_resistor_length");
Xml.Layer polyLayer = t.findLayer(poly_layer.name);
for (int i = 0; i < 2; i++)
{
Xml.Layer selectLayer;
WizardField selectWF;
PrimitiveNode.Function func;
nodesList.clear();
nodePorts.clear();
if (i==Technology.P_TYPE)
{
selectLayer = t.findLayer(pplus_layer.name);
selectWF = pplus_overhang_diff;
func = PrimitiveNode.Function.RESPPOLY;
}
else
{
selectLayer = t.findLayer(nplus_layer.name);
selectWF = nplus_overhang_diff;
func = PrimitiveNode.Function.RESNPOLY;
}
// poly layer
polyNoScaled = 2 * (contact_poly_overhang.value) + contact_size.value;
polyL = scaledValue(polyRL.value /2 + polyNoScaled);
polyWNoScaled = contact_size.value/2 + diff_contact_overhang.value;
polyW = scaledValue(polyWNoScaled);
nodesList.add(makeXmlNodeLayer(polyL, polyL, polyW, polyW, polyLayer, Poly.Type.FILLED, true, true, 0));
// select layer
double selectOverhang = scaledValue(selectWF.value);
double selectL = selectOverhang + polyL;
double selectW = selectOverhang + polyW;
nodesList.add(makeXmlNodeLayer(selectL, selectL, selectW, selectW, selectLayer, Poly.Type.FILLED, true, true, 0));
addMetalElements(t, polyConLayer, contact_array_spacing.value, polyRL, contact_poly_overhang, nodesList, nodePorts);
sox = scaledValue(selectWF.value + polyNoScaled);
soy = scaledValue(selectWF.value);
n = makeXmlPrimitive(t.nodeGroups, diffNames[i]+"-Poly-Resistor", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy),
nodesList, nodePorts, null, false);
g.addElement(n, diffNames[i]+"-RPoly");
}
/*************************************/
// Analog unsilicided Poly Resistors (no hi res)
/*************************************/
WizardField silicide_overhang = findWizardField("silicide_overhang");
for (int i = 0; i < 2; i++)
{
Xml.Layer selectLayer;
PrimitiveNode.Function func;
WizardField selectWF;
if (i==Technology.P_TYPE)
{
selectLayer = t.findLayer(pplus_layer.name);
selectWF = pplus_overhang_diff;
func = PrimitiveNode.Function.RESPNSPOLY;
}
else
{
selectLayer = t.findLayer(nplus_layer.name);
selectWF = nplus_overhang_diff;
func = PrimitiveNode.Function.RESNNSPOLY;
}
nodesList.clear();
nodePorts.clear();
// poly layer
// polyNoScaled = 2 * (contact_poly_overhang.value) + contact_size.value;
polyNoScaled = contact_poly_overhang.value + silicide_overhang.value + contact_size.value; // due to silicide block not covering cut
polyL = scaledValue(polyRL.value /2 + polyNoScaled);
polyWNoScaled = contact_size.value/2 + diff_contact_overhang.value;
polyW = scaledValue(polyWNoScaled);
nodesList.add(makeXmlNodeLayer(polyL, polyL, polyW, polyW, polyLayer, Poly.Type.FILLED, true, true, 0));
// select layer
double selectOverhang = scaledValue(selectWF.value);
double selectL = selectOverhang + polyL;
double selectW = selectOverhang + polyW;
nodesList.add(makeXmlNodeLayer(selectL, selectL, selectW, selectW, selectLayer, Poly.Type.FILLED, true, true, 0));
WizardField len = polyRL;
// fake WizardField to compensate silicide_overhang.value - contact_poly_overhang.value
if (silicide_overhang.value > contact_poly_overhang.value)
{
len = new WizardField(polyRL.value + 2*(silicide_overhang.value - contact_poly_overhang.value), "modified polyRL");
}
// silicide_block layer
Xml.Layer silicideLayer = t.findLayer(marking_layer.name);
double silicideOverhang = scaledValue(silicide_overhang.value);
double silicideL = scaledValue(polyRL.value /2); // silicideOverhang + selectL;
double silicideW = silicideOverhang + selectW;
nodesList.add(makeXmlNodeLayer(silicideL, silicideL, silicideW, silicideW, silicideLayer, Poly.Type.FILLED, true, true, 0));
addMetalElements(t, polyConLayer, contact_array_spacing.value, len, contact_poly_overhang, nodesList, nodePorts);
sox = scaledValue(/*silicide_overhang.value + */selectWF.value + polyNoScaled);
soy = scaledValue(silicide_overhang.value + selectWF.value);
n = makeXmlPrimitive(t.nodeGroups, diffNames[i]+"-No-Silicide-Poly-Resistor",
func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy),
nodesList, nodePorts, null, false);
g.addElement(n, diffNames[i]+"-RNSPoly");
}
}
private void addMetalElements(Xml.Technology t, Xml.Layer conLayer, double spacing, WizardField width, WizardField overhang,
List<Xml.NodeLayer> nodesList, List<Xml.PrimitivePort> nodePorts)
{
Xml.Layer m1Layer = t.findLayer("Metal-1");
List<String> portNames = new ArrayList<String>();
portNames.add(m1Layer.name);
// metal left
double m1Y = scaledValue(contact_metal_overhang_all_sides.value + contact_size.value/2);
double m1X = scaledValue(2*contact_metal_overhang_all_sides.value + contact_size.value);
double m1Distance = scaledValue(width.value/2 + overhang.value - contact_metal_overhang_all_sides.value);
nodesList.add(makeXmlNodeLayer((m1Distance + m1X), -1, -m1Distance, -1, m1Y, -1, m1Y, 1, m1Layer,
Poly.Type.FILLED, true, true, 0));
// right metal
nodesList.add(makeXmlNodeLayer(-m1Distance, 1, (m1Distance + m1X), 1, m1Y, -1, m1Y, 1, m1Layer,
Poly.Type.FILLED, true, true, 1));
// left port
double contSize = scaledValue(contact_size.value);
double cutSizeHalf = scaledValue(contact_size.value /2);
double cutStart = scaledValue(width.value /2 + overhang.value);
Xml.PrimitivePort port = makeXmlPrimitivePort("left", 0, 180, 0, null,
-(cutStart + contSize), -1, -cutStart, -1, -cutSizeHalf, -1, cutSizeHalf, 1, portNames);
nodePorts.add(port);
// right port
port = makeXmlPrimitivePort("right", 0, 180, 1, null,
cutStart, 1, (cutStart + contSize), 1, -cutSizeHalf, -1, cutSizeHalf, 1, portNames);
nodePorts.add(port);
// Cuts
double cutEnd = scaledValue(width.value/2 + overhang.value);
double cutEndY = 0; // not sure why has to be zero and not scaledValue(contact_size.value/2);
// left
nodesList.add(makeXmlMulticut(cutEnd+contSize, -1, -cutEnd, -1, cutEndY, -1, cutEndY, 1,
conLayer, contSize, spacing, spacing));
// right
nodesList.add(makeXmlMulticut(-cutEnd, 1, cutEnd+contSize, 1, cutEndY, -1, cutEndY, 1,
conLayer, contSize, spacing, spacing));
}
private void addStandardLayers(Xml.Technology t, Map<Xml.Layer, WizardField> layerMap,
int[] nullPattern, int[] dexclPattern)
{
Color contact_colour = new Color(100,100,100); // darker gray
Color nplus_colour = new Color(224,238,224);
Color pplus_colour = new Color(224,224,120);
Color nwell_colour = new Color(140,140,140);
//
// Adding necessary layers
//
// Poly
EGraphics graph = new EGraphics(false, false, null, 1, 0, 0, 0, 1, true, nullPattern);
Xml.Layer polyLayer = makeXmlLayer(t.layers, layerMap, poly_layer.name, Layer.Function.POLY1, 0, graph,
poly_width, true, true);
// PolyGate
Xml.Layer polyGateLayer = makeXmlLayer(t.layers, layerMap, poly_layer.name+"Gate", Layer.Function.GATE, 0, graph,
poly_width, true, false); // false for the port otherwise it won't find any type
if (isComplexCase())
{
// exclusion layer poly
graph = new EGraphics(true, true, null, 1, 0, 0, 0, 1, true, dexclPattern);
Xml.Layer exclusionPolyLayer = makeXmlLayer(t.layers, "DEXCL-"+poly_layer.name, Layer.Function.DEXCLPOLY1, 0, graph,
2*poly_width.value, true, false);
makeLayerGDS(t, exclusionPolyLayer, "150/21");
}
// PolyCon and DiffCon
graph = new EGraphics(false, false, null, 0, contact_colour.getRed(), contact_colour.getGreen(),
contact_colour.getBlue(), 0.5, true, nullPattern);
// PolyCon
Xml.Layer polyConLayer = makeXmlLayer(t.layers, layerMap, "Poly-Cut", Layer.Function.CONTACT1,
Layer.Function.CONPOLY, graph, contact_size, true, false);
// DiffCon
Xml.Layer diffConLayer = makeXmlLayer(t.layers, layerMap, diff_layer.name+"-Cut", Layer.Function.CONTACT1,
Layer.Function.CONDIFF, graph, contact_size, true, false);
// P-Diff and N-Diff
graph = new EGraphics(false, false, null, 2, 0, 0, 0, 1, true, nullPattern);
// N-Diff
Xml.Layer diffNLayer = makeXmlLayer(t.layers, layerMap, "N-"+ diff_layer.name, Layer.Function.DIFFN, 0, graph,
diff_width, true, true, "N-"+ diff_layer.name, "N-Well", "S-N-Well");
// P-Diff dd
Xml.Layer diffPLayer = makeXmlLayer(t.layers, layerMap, "P-"+ diff_layer.name, Layer.Function.DIFFP, 0, graph,
diff_width, true, true, "P-"+ diff_layer.name, "P-Well", "S-P-Well");
// NPlus and PPlus
int [] patternSlash = new int[] { 0x1010, // X X
0x2020, // X X
0x4040, // X X
0x8080, // X X
0x0101, // X X
0x0202, // X X
0x0404, // X X
0x0808, // X X
0x1010, // X X
0x2020, // X X
0x4040, // X X
0x8080, // X X
0x0101, // X X
0x0202, // X X
0x0404, // X X
0x0808};
int [] patternBackSlash = new int[] { 0x0202, // X X
0x0101, // X X
0x8080, // X X
0x4040, // X X
0x2020, // X X
0x1010, // X X
0x0808, // X X
0x0404, // X X
0x0202, // X X
0x0101, // X X
0x8080, // X X
0x4040, // X X
0x2020, // X X
0x1010, // X X
0x0808, // X X
0x0404};
int[] patternDots = new int[] {
0x0202, // X X
0x0000, //
0x2020, // X X
0x0000, //
0x0202, // X X
0x0000, //
0x2020, // X X
0x0000, //
0x0202, // X X
0x0000, //
0x2020, // X X
0x0000, //
0x0202, // X X
0x0000, //
0x2020, // X X
0x0000}; //
int[] patternDotsShift = new int[] {
0x0000, //
0x2020, // X X
0x0000, //
0x0202, // X X
0x0000, //
0x2020, // X X
0x0000, //
0x0202, // X X
0x0000, //
0x2020, // X X
0x0000, //
0x0202, // X X
0x0000, //
0x2020, // X X
0x0000, //
0x0202}; // X X
// NPlus
graph = new EGraphics(true, true, null, 0, nplus_colour.getRed(), nplus_colour.getGreen(),
nplus_colour.getBlue(), 1, true, patternSlash);
Xml.Layer nplusLayer = makeXmlLayer(t.layers, layerMap, nplus_layer.name, Layer.Function.IMPLANTN, 0, graph,
nplus_width, true, false);
// PPlus
graph = new EGraphics(true, true, null, 0, pplus_colour.getRed(), pplus_colour.getGreen(),
pplus_colour.getBlue(), 1, true, patternDots);
Xml.Layer pplusLayer = makeXmlLayer(t.layers, layerMap, pplus_layer.name, Layer.Function.IMPLANTP, 0, graph,
pplus_width, true, false);
// N-Well
graph = new EGraphics(true, true, null, 0, nwell_colour.getRed(), nwell_colour.getGreen(),
nwell_colour.getBlue(), 1, true, patternDotsShift);
Xml.Layer nwellLayer = makeXmlLayer(t.layers, layerMap, nwell_layer.name, Layer.Function.WELLN, 0, graph,
nwell_width, true, false);
// P-Well
graph = new EGraphics(true, true, null, 0, nwell_colour.getRed(), nwell_colour.getGreen(),
nwell_colour.getBlue(), 1, true, patternBackSlash);
Xml.Layer pwellLayer = makeXmlLayer(t.layers, layerMap, "P-Well", Layer.Function.WELLP, 0, graph,
nwell_width, true, false);
}
private void addStandardElements(Xml.Technology t, Map<Xml.Layer, WizardField> layerMap,
List<Xml.Layer> metalLayers, List<PaletteGroup> allGroups)
{
/**************************** POLY Nodes/Arcs ***********************************************/
// poly arc
double ant = (int)Math.round(poly_antenna_ratio) | 200;
List<PaletteGroup> polysGroup = new ArrayList<PaletteGroup>();
PaletteGroup polyGroup = new PaletteGroup();
polysGroup.add(polyGroup);
List<String> portNames = new ArrayList<String>();
Xml.Layer polyLayer = t.findLayer(poly_layer.name);
Xml.Layer polyGateLayer = t.findLayer(poly_layer.name+"Gate");
Xml.Layer polyConLayer = t.findLayer("Poly-Cut");
Xml.Layer diffConLayer = t.findLayer(diff_layer.name+"-Cut");
Xml.Layer nwellLayer = t.findLayer(nwell_layer.name);
Xml.Layer pwellLayer = t.findLayer("P-Well");
Xml.Layer nplusLayer = t.findLayer(nplus_layer.name);
Xml.Layer pplusLayer = t.findLayer(pplus_layer.name);
Xml.Layer diffNLayer = t.findLayer("N-"+ diff_layer.name);
Xml.Layer diffPLayer = t.findLayer("P-"+ diff_layer.name);
polyGroup.addArc(makeXmlArc(t, polyLayer.name, ArcProto.Function.getPoly(1), ant,
makeXmlArcLayer(polyLayer, poly_width)));
// poly pin
double hla = scaledValue(poly_width.value / 2);
polyGroup.addPinOrResistor(makeXmlPrimitivePin(t, polyLayer.name, hla, null, // new SizeOffset(hla, hla, hla, hla),
null, makeXmlNodeLayer(hla, hla, hla, hla, polyLayer, Poly.Type.CROSSED)), null);
if (getSecondPolyFlag()) createSecondPolyElements(t, layerMap, polysGroup);
if (getAnalogFlag()) createAnalogElements(t, metalLayers, polysGroup);
Xml.Layer m1Layer = metalLayers.get(0);
// poly contact
portNames.clear();
portNames.add(polyLayer.name);
portNames.add(m1Layer.name);
hla = scaledValue((contact_size.value /2 + contact_poly_overhang.value));
double contSize = scaledValue(contact_size.value);
double contSpacing = scaledValue(contact_spacing.value);
double contArraySpacing = scaledValue(contact_array_spacing.value);
double metal1Over = scaledValue(contact_size.value /2 + contact_metal_overhang_all_sides.value);
// only for standard cases when getExtraInfoFlag() is false
if (!isComplexCase())
{
if (POLY_CONTACT_SAME_AS_DIFF_CONTACT) {
polyGroup.addElement(makeContactSeries(t.nodeGroups, polyLayer.name, contSize, polyConLayer, contSpacing, contArraySpacing,
scaledValue(contact_poly_overhang.value), polyLayer,
scaledValue(contact_metal_overhang_all_sides.value), m1Layer), null);
} else {
if (via_overhang.length > 0)
polyGroup.addElement(makeContactSeries(t.nodeGroups, polyLayer.name, contSize, polyConLayer, contSpacing, contArraySpacing,
scaledValue(contact_poly_overhang.value), polyLayer,
scaledValue(via_overhang[0].value), m1Layer), null);
else
System.out.println("Not via 0 layer");
}
}
/**************************** N/P-Diff Nodes/Arcs/Group ***********************************************/
PaletteGroup[] diffPalette = new PaletteGroup[2];
diffPalette[0] = new PaletteGroup(); diffPalette[1] = new PaletteGroup();
PaletteGroup[] wellPalette = new PaletteGroup[2];
wellPalette[0] = new PaletteGroup(); wellPalette[1] = new PaletteGroup();
// ndiff/pdiff pins
hla = scaledValue((contact_size.value /2 + diff_contact_overhang.value));
double nsel = scaledValue(contact_size.value /2 + diff_contact_overhang.value + nplus_overhang_diff.value);
double psel = scaledValue(contact_size.value /2 + diff_contact_overhang.value + pplus_overhang_diff.value);
double nwell = scaledValue(contact_size.value /2 + diff_contact_overhang.value + nwell_overhang_diff_p.value);
double nso = scaledValue(nwell_overhang_diff_p.value /*+ diff_contact_overhang.v*/); // valid for elements that have nwell layers
double pso = (!pSubstrateProcess)?nso:scaledValue(nplus_overhang_diff.value/* + diff_contact_overhang.v*/);
// ndiff/pdiff contacts
String[] diffNames = {"P", "N"};
double[] sos = {nso, pso};
double[] sels = {psel, nsel};
Xml.Layer[] diffLayers = {diffPLayer, diffNLayer};
Xml.Layer[] plusLayers = {pplusLayer, nplusLayer};
// Xml.Layer pol2yLayer = t.findLayer(poly2_layer.name);
// Active and poly contacts. They are defined first that the Full types
for (Map.Entry<String,List<Contact>> e : otherContacts.entrySet())
{
addContactsOrCapacitors(t, e.getValue(), metalLayers, diffPalette, wellPalette, polyGroup, false);
// // generic contacts
// String name = null;
//
// for (Contact c : e.getValue())
// {
// Xml.Layer ly = null, lx = null;
// Xml.Layer conLay = diffConLayer;
// PaletteGroup g = null;
// ContactNode metalLayer = c.layers.get(0);
// ContactNode otherLayer = c.layers.get(1);
// String extraName = "";
//
// if (!TextUtils.isANumber(metalLayer.layer)) // horizontal must be!
// {
// assert (TextUtils.isANumber(otherLayer.layer));
// metalLayer = c.layers.get(1);
// otherLayer = c.layers.get(0);
// }
//
// int m1 = Integer.valueOf(metalLayer.layer);
// ly = metalLayers.get(m1-1);
// String layerName = otherLayer.layer;
// if (layerName.equals(diffLayers[0].name))
// {
// lx = diffLayers[0];
// g = diffPalette[0];
// extraName = "P";
// }
// else if (layerName.equals(diffLayers[1].name))
// {
// lx = diffLayers[1];
// g = diffPalette[1];
// extraName = "N";
// }
// else if (layerName.equals(polyLayer.name))
// {
// lx = polyLayer;
// conLay = polyConLayer;
// g = polyGroup;
// }
// else if (getSecondPolyFlag() && layerName.equals(pol2yLayer.name))
// {
// lx = pol2yLayer;
// conLay = polyConLayer;
// g = polyGroup;
// }
// else
// assert(false); // it should not happen
// double h1x = scaledValue(contact_size.value /2 + metalLayer.valueX.value);
// double h1y = scaledValue(contact_size.value /2 + metalLayer.valueY.value);
// double h2x = scaledValue(contact_size.value /2 + otherLayer.valueX.value);
// double h2y = scaledValue(contact_size.value /2 + otherLayer.valueY.value);
// double longX = (Math.abs(metalLayer.valueX.value - otherLayer.valueX.value));
// double longY = (Math.abs(metalLayer.valueY.value - otherLayer.valueY.value));
//
// PrimitiveNode.Function func = PrimitiveNode.Function.CONTACT;
// Xml.NodeLayer[] nodes = new Xml.NodeLayer[c.layers.size() + 1]; // all plus cut
// int count = 0;
//
// // cut
// nodes[count++] = makeXmlMulticut(conLay, contSize, contSpacing, contArraySpacing);
// // metal
// nodes[count++] = makeXmlNodeLayer(h1x, h1x, h1y, h1y, ly, Poly.Type.FILLED); // layer1
// // active or poly
// nodes[count++] = makeXmlNodeLayer(h2x, h2x, h2y, h2y, lx, Poly.Type.FILLED); // layer2
//
// Xml.Layer otherLayerPort = lx;
//
// for (int i = 2; i < c.layers.size(); i++) // rest of layers. Either select or well.
// {
// ContactNode node = c.layers.get(i);
// Xml.Layer lz = t.findLayer(node.layer);
//
// if ((lz == pwellLayer && lx == diffLayers[0]) ||
// (lz == nwellLayer && lx == diffLayers[1])) // well contact
// {
// otherLayerPort = lz;
// if (lz == pwellLayer)
// {
// g = wellPalette[0];
// func = getWellContactFunction(Technology.P_TYPE);
// extraName = "PW"; // W for well
// }
// else // nwell
// {
// g = wellPalette[1];
// func = getWellContactFunction(Technology.N_TYPE);
// extraName = "NW"; // W for well
// }
// }
// if (pSubstrateProcess && lz == pwellLayer)
// continue; // skip this layer
//
// double h3x = scaledValue(contact_size.value /2 + node.valueX.value);
// double h3y = scaledValue(contact_size.value /2 + node.valueY.value);
// nodes[count++] = makeXmlNodeLayer(h3x, h3x, h3y, h3y, lz, Poly.Type.FILLED);
//
// // This assumes no well is defined
// double longXLocal = (Math.abs(node.valueX.value - otherLayer.valueX.value));
// double longYLocal = (Math.abs(node.valueY.value - otherLayer.valueY.value));
// if (DBMath.isGreaterThan(longXLocal, longX))
// longX = longXLocal;
// if (DBMath.isGreaterThan(longYLocal, longY))
// longY = longYLocal;
// }
// longX = scaledValue(longX);
// longY = scaledValue(longY);
//
// // prt names now after determing wheter is a diff or well contact
// portNames.clear();
//// if (!pSubstrateProcess || otherLayerPort == pwellLayer)
// portNames.add(otherLayerPort.name);
// portNames.add(ly.name); // always should represent the metal1
// name = ly.name + "-" + otherLayerPort.name;
//
//
// // some primitives might not have prefix. "-" should not be in the prefix to avoid
// // being displayed in the palette
// String p = (c.prefix == null || c.prefix.equals("")) ? "" : c.prefix + "-";
// g.addElement(makeXmlPrimitiveCon(t.nodeGroups, p + name, func, -1, -1,
// new SizeOffset(longX, longX, longY, longY), portNames,
// nodes), p + extraName); // contact
// }
}
// ndiff/pdiff contact
for (int i = 0; i < 2; i++)
{
portNames.clear();
portNames.add(diffLayers[i].name);
portNames.add(m1Layer.name);
String composeName = diffNames[i] + "-" + diff_layer.name; //Diff";
Xml.NodeLayer wellNode, wellNodePin;
ArcProto.Function arcF;
Xml.ArcLayer arcL;
WizardField arcVal;
if (i == Technology.P_TYPE)
{
wellNodePin = makeXmlNodeLayer(nwell, nwell, nwell, nwell, nwellLayer, Poly.Type.CROSSED);
wellNode = makeXmlNodeLayer(nwell, nwell, nwell, nwell, nwellLayer, Poly.Type.FILLED);
arcF = ArcProto.Function.DIFFP;
arcL = makeXmlArcLayer(nwellLayer, diff_width, nwell_overhang_diff_p);
arcVal = pplus_overhang_diff;
}
else
{
wellNodePin = (!pSubstrateProcess)?makeXmlNodeLayer(nwell, nwell, nwell, nwell, pwellLayer, Poly.Type.CROSSED):null;
wellNode = (!pSubstrateProcess)?makeXmlNodeLayer(nwell, nwell, nwell, nwell, pwellLayer, Poly.Type.FILLED):null;
arcF = ArcProto.Function.DIFFN;
arcL = (!pSubstrateProcess)?makeXmlArcLayer(pwellLayer, diff_width, nwell_overhang_diff_p):null;
arcVal = nplus_overhang_diff;
}
PaletteGroup diffG = diffPalette[i];
// active arc
diffG.addArc(makeXmlArc(t, composeName, arcF, 0,
makeXmlArcLayer(diffLayers[i], diff_width),
makeXmlArcLayer(plusLayers[i], diff_width, arcVal),
arcL));
// active pin
diffG.addPinOrResistor(makeXmlPrimitivePin(t, composeName, hla,
new SizeOffset(sos[i], sos[i], sos[i], sos[i]), null,
makeXmlNodeLayer(hla, hla, hla, hla, diffLayers[i], Poly.Type.CROSSED),
makeXmlNodeLayer(sels[i], sels[i], sels[i], sels[i], plusLayers[i], Poly.Type.CROSSED),
wellNodePin), null);
// F stands for full (all layers)
diffG.addElement(makeXmlPrimitiveCon(t.nodeGroups, "F-"+composeName, PrimitiveNode.Function.CONTACT,
hla, hla, new SizeOffset(sos[i], sos[i], sos[i], sos[i]), portNames,
makeXmlNodeLayer(metal1Over, metal1Over, metal1Over, metal1Over, m1Layer, Poly.Type.FILLED), // meta1 layer
makeXmlNodeLayer(hla, hla, hla, hla, diffLayers[i], Poly.Type.FILLED), // active layer
makeXmlNodeLayer(sels[i], sels[i], sels[i], sels[i], plusLayers[i], Poly.Type.FILLED), // select layer
wellNode, // well layer
makeXmlMulticut(diffConLayer, contSize, contSpacing, contArraySpacing)), "Full-" + diffNames[i]); // contact
}
/**************************** N/P-Well Contacts ***********************************************/
nwell = scaledValue(contact_size.value /2 + diff_contact_overhang.value + nwell_overhang_diff_n.value);
nso = scaledValue(/*diff_contact_overhang.v +*/ nwell_overhang_diff_n.value); // valid for elements that have nwell layers
pso = (!pSubstrateProcess)?nso:scaledValue(/*diff_contact_overhang.v +*/ pplus_overhang_strap.value);
double[] wellSos = {pso, nso};
Xml.Layer[] wellLayers = {pwellLayer, nwellLayer};
double nselW = scaledValue(contact_size.value /2 + diff_contact_overhang.value + nplus_overhang_strap.value);
double pselW = scaledValue(contact_size.value /2 + diff_contact_overhang.value + pplus_overhang_strap.value);
double[] wellSels = {pselW, nselW};
// nwell/pwell contact
for (int i = 0; i < 2; i++)
{
String composeName = diffNames[i] + "-Well";
Xml.NodeLayer wellNodeLayer = null, wellNodePinLayer = null;
PaletteGroup g = wellPalette[i];
PrimitiveNode.Function func = getWellContactFunction(i);
Xml.ArcLayer arcL;
WizardField arcVal;
portNames.clear();
if (i == Technology.P_TYPE)
{
if (!pSubstrateProcess)
{
wellNodePinLayer = makeXmlNodeLayer(nwell, nwell, nwell, nwell, pwellLayer, Poly.Type.CROSSED);
wellNodeLayer = makeXmlNodeLayer(nwell, nwell, nwell, nwell, pwellLayer, Poly.Type.FILLED);
}
portNames.add(pwellLayer.name);
arcL = (!pSubstrateProcess)?makeXmlArcLayer(pwellLayer, diff_width, nwell_overhang_diff_p):null;
arcVal = pplus_overhang_diff;
}
else
{
portNames.add(nwellLayer.name);
wellNodePinLayer = makeXmlNodeLayer(nwell, nwell, nwell, nwell, nwellLayer, Poly.Type.CROSSED);
wellNodeLayer = makeXmlNodeLayer(nwell, nwell, nwell, nwell, nwellLayer, Poly.Type.FILLED);
arcL = makeXmlArcLayer(nwellLayer, diff_width, nwell_overhang_diff_p);
arcVal = nplus_overhang_diff;
}
portNames.add(m1Layer.name);
// three layers arcs. This is the first port defined so it will be the default in the palette
g.addArc(makeXmlArc(t, composeName, ArcProto.Function.WELL, 0,
makeXmlArcLayer(diffLayers[i], diff_width),
makeXmlArcLayer(plusLayers[i], diff_width, arcVal),
arcL));
// simple arc. S for simple
g.addArc(makeXmlArc(t, "S-"+composeName, ArcProto.Function.WELL, 0,
makeXmlArcLayer(wellLayers[i], diff_width, nwell_overhang_diff_p)));
// well pin
List<String> arcNames = new ArrayList<String>();
arcNames.add(composeName); arcNames.add("S-"+composeName);
g.addPinOrResistor(makeXmlPrimitivePin(t, composeName, hla,
new SizeOffset(wellSos[i], wellSos[i], wellSos[i], wellSos[i]), arcNames,
makeXmlNodeLayer(hla, hla, hla, hla, diffLayers[i], Poly.Type.CROSSED),
makeXmlNodeLayer(sels[i], sels[i], sels[i], sels[i], plusLayers[i], Poly.Type.CROSSED),
wellNodePinLayer), null);
// well contact
// F stands for full
g.addElement(makeXmlPrimitiveCon(t.nodeGroups, "F-"+composeName, func,
hla, hla, new SizeOffset(wellSos[i], wellSos[i], wellSos[i], wellSos[i]), portNames,
makeXmlNodeLayer(metal1Over, metal1Over, metal1Over, metal1Over, m1Layer, Poly.Type.FILLED), // meta1 layer
makeXmlNodeLayer(hla, hla, hla, hla, diffLayers[i], Poly.Type.FILLED), // active layer
makeXmlNodeLayer(wellSels[i], wellSels[i], wellSels[i], wellSels[i], plusLayers[i], Poly.Type.FILLED), // select layer
wellNodeLayer, // well layer
makeXmlMulticut(diffConLayer, contSize, contSpacing, contArraySpacing)), "Full-"+diffNames[i] + "W"); // contact
}
/**************************** Transistors ***********************************************/
/** Transistors **/
// write the transistors
List<Xml.NodeLayer> nodesList = new ArrayList<Xml.NodeLayer>();
List<Xml.PrimitivePort> nodePorts = new ArrayList<Xml.PrimitivePort>();
EPoint minFullSize = null; //EPoint.fromLambda(0, 0); // default zero horizontalFlag
PaletteGroup[] transPalette = new PaletteGroup[2];
for(int i = 0; i < 2; i++)
{
String name;
double selecty = 0, selectx = 0;
Xml.Layer wellLayer = null, activeLayer, selectLayer;
double sox = 0, soy = 0;
double impx = scaledValue((gate_width.value)/2);
double impy = scaledValue((gate_length.value +diff_poly_overhang.value *2)/2);
double nwell_overhangX = 0, nwell_overhangY = 0;
PaletteGroup g = new PaletteGroup();
transPalette[i] = g;
double protectDist = scaledValue(poly_protection_spacing.value);
double extraSelX = 0, extraSelY = 0;
PrimitiveNode.Function func = null, prFunc = null, wrFunc;
if (i==Technology.P_TYPE)
{
name = "P";
nwell_overhangY = nwell_overhangX = nwell_overhang_diff_n.value;
wellLayer = nwellLayer;
activeLayer = diffPLayer;
selectLayer = pplusLayer;
extraSelX = pplus_overhang_poly.value;
extraSelY = pplus_overhang_diff.value;
func = PrimitiveNode.Function.TRAPMOS;
prFunc = PrimitiveNode.Function.RESPPOLY;
wrFunc = PrimitiveNode.Function.RESPWELL;
}
else
{
name = "N";
activeLayer = diffNLayer;
selectLayer = nplusLayer;
extraSelX = nplus_overhang_poly.value;
extraSelY = nplus_overhang_diff.value;
func = PrimitiveNode.Function.TRANMOS;
prFunc = PrimitiveNode.Function.RESNPOLY;
wrFunc = PrimitiveNode.Function.RESNWELL;
if (!pSubstrateProcess)
{
nwell_overhangY = nwell_overhangX = nwell_overhang_diff_p.value;
wellLayer = pwellLayer;
}
else
{
nwell_overhangX = poly_endcap.value +extraSelX;
nwell_overhangY = extraSelY;
}
}
selectx = scaledValue(gate_width.value /2+poly_endcap.value +extraSelX);
selecty = scaledValue(gate_length.value /2+diff_poly_overhang.value +extraSelY);
// Using P values in transistors
double wellx = scaledValue((gate_width.value /2+nwell_overhangX));
double welly = scaledValue((gate_length.value /2+diff_poly_overhang.value +nwell_overhangY));
sox = scaledValue(nwell_overhangX);
soy = scaledValue(diff_poly_overhang.value +nwell_overhangY);
if (DBMath.isLessThan(wellx, selectx))
{
sox = scaledValue(poly_endcap.value +extraSelX);
wellx = selectx;
}
if (DBMath.isLessThan(welly, selecty))
{
soy = scaledValue(diff_poly_overhang.value +extraSelY);
welly = selecty;
}
nodesList.clear();
nodePorts.clear();
portNames.clear();
// Gate layer Electrical
double gatey = scaledValue(gate_length.value /2);
double gatex = impx;
// Poly layers
// left electrical
double endPolyx = scaledValue((gate_width.value +poly_endcap.value *2)/2);
double endPolyy = gatey;
double endLeftOrRight = -impx; // for horizontal transistors. Default
double endTopOrBotton = endPolyy; // for horizontal transistors. Default
double diffX = 0, diffY = scaledValue(gate_length.value /2+gate_contact_spacing.value +contact_size.value /2); // impy
double xSign = 1, ySign = -1;
double polyX = endPolyx, polyY = 0;
if (!horizontalFlag) // swap the numbers to get vertical transistors
{
double tmp;
tmp = impx; impx = impy; impy = tmp;
tmp = wellx; wellx = welly; welly = tmp;
tmp = sox; sox = soy; soy = tmp;
tmp = selectx; selectx = selecty; selecty = tmp;
tmp = gatex; gatex = gatey; gatey = tmp;
tmp = endPolyx; endPolyx = endPolyy; endPolyy = tmp;
tmp = diffX; diffX = diffY; diffY = tmp;
tmp = polyX; polyX = polyY; polyY = tmp;
tmp = xSign; xSign = ySign; ySign = tmp;
endLeftOrRight = endPolyx;
endTopOrBotton = -impx;
}
// Well layer
Xml.NodeLayer xTranWellLayer = null;
if (wellLayer != null)
{
xTranWellLayer = (makeXmlNodeLayer(wellx, wellx, welly, welly, wellLayer, Poly.Type.FILLED));
nodesList.add(xTranWellLayer);
}
// Active layers
nodesList.add(makeXmlNodeLayer(impx, impx, impy, impy, activeLayer, Poly.Type.FILLED, true, false, -1));
// electrical active layers
nodesList.add(makeXmlNodeLayer(impx, impx, impy, 0, activeLayer, Poly.Type.FILLED, false, true, 3)); // bottom
nodesList.add(makeXmlNodeLayer(impx, impx, 0, impy, activeLayer, Poly.Type.FILLED, false, true, 1)); // top
// Diff port
portNames.clear();
portNames.add(activeLayer.name);
Xml.PrimitivePort diffTopPort = makeXmlPrimitivePort("diff-top", 90, 90, 1, minFullSize,
diffX, -1, diffX, 1, diffY, 1, diffY, 1, portNames);
// bottom port
Xml.PrimitivePort diffBottomPort = makeXmlPrimitivePort("diff-bottom", 270, 90, 2, minFullSize,
xSign*diffX, -1, xSign*diffX, 1, ySign*diffY, -1, ySign*diffY, -1, portNames);
// Electric layers
// Gate layer Electrical
nodesList.add(makeXmlNodeLayer(gatex, gatex, gatey, gatey, polyGateLayer, Poly.Type.FILLED, false, true, -1));
// Poly layers
// left electrical
nodesList.add(makeXmlNodeLayer(endPolyx, endLeftOrRight, endPolyy, endTopOrBotton, polyLayer,
Poly.Type.FILLED, false, true, 0));
// right electrical
nodesList.add(makeXmlNodeLayer(endLeftOrRight, endPolyx, endTopOrBotton, endPolyy, polyLayer,
Poly.Type.FILLED, false, true, 2));
// non-electrical poly (just one poly layer)
nodesList.add(makeXmlNodeLayer(endPolyx, endPolyx, endPolyy, endPolyy, polyLayer, Poly.Type.FILLED, true, false, -1));
// Poly port
portNames.clear();
portNames.add(polyLayer.name);
Xml.PrimitivePort polyLeftPort = makeXmlPrimitivePort("poly-left", 180, 90, 0, minFullSize,
ySign*polyX, -1, ySign*polyX,
-1, xSign*polyY, -1, xSign*polyY, 1, portNames);
// right port
Xml.PrimitivePort polyRightPort = makeXmlPrimitivePort("poly-right", 0, 180, 0, minFullSize,
polyX, 1, polyX, 1, polyY, -1, polyY, 1, portNames);
// Select layer
Xml.NodeLayer xTranSelLayer = (makeXmlNodeLayer(selectx, selectx, selecty, selecty, selectLayer, Poly.Type.FILLED));
nodesList.add(xTranSelLayer);
//One (undocumented) requirement of transistors is that the ports must appear in the
//order: Poly-left, Diff-top, Poly-right, Diff-bottom. This requirement is
//because of the methods Technology.getTransistorGatePort(),
//Technology.getTransistorAltGatePort(), Technology.getTransistorSourcePort(),
//and Technology.getTransistorDrainPort().
// diff-top = 1, diff-bottom = 2, polys=0
// ports in the correct order: Poly-left, Diff-top, Poly-right, Diff-bottom
nodePorts.add(polyLeftPort);
nodePorts.add(diffTopPort);
nodePorts.add(polyRightPort);
nodePorts.add(diffBottomPort);
// Standard Transistor
Xml.PrimitiveNodeGroup n = makeXmlPrimitive(t.nodeGroups, name + "-Transistor", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, name);
// Extra transistors which don't have select nor well
// Extra protection poly. No ports are necessary.
if (isComplexCase())
{
/*************************************/
// Short transistors
// Adding extra transistors whose select and well are aligned with poly along the X axis
nodesList.remove(xTranSelLayer);
double shortSelectX = scaledValue(gate_width.value /2+poly_endcap.value);
xTranSelLayer = (makeXmlNodeLayer(shortSelectX, shortSelectX, selecty, selecty, selectLayer, Poly.Type.FILLED));
nodesList.add(xTranSelLayer);
double shortSox = sox;
shortSox = scaledValue(poly_endcap.value);
if (wellLayer != null)
{
nodesList.remove(xTranWellLayer);
xTranWellLayer = (makeXmlNodeLayer(shortSelectX, shortSelectX, welly, welly, wellLayer, Poly.Type.FILLED));
nodesList.add(xTranWellLayer);
}
n = makeXmlPrimitive(t.nodeGroups, name + "-Transistor-S", func, 0, 0, 0, 0,
new SizeOffset(shortSox, shortSox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, name + "-S");
/*************************************/
// Short transistors with VTH and VTL
double vthlx = scaledValue(gate_width.value /2+vthl_diff_overhang.value);
double vthly = scaledValue(gate_length.value /2+ vthl_poly_overhang.value);
// VTH Transistor
String tmp = "VTH-" + name;
Xml.NodeLayer nl = addXmlNodeLayer(nodesList, t, tmp, vthlx, vthly);
n = makeXmlPrimitive(t.nodeGroups, tmp + "-Transistor-S", func, 0, 0, 0, 0,
new SizeOffset(shortSox, shortSox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, tmp + "-S");
// VTL Transistor
nodesList.remove(nl);
tmp = "VTL-" + name;
nl = addXmlNodeLayer(nodesList, t, tmp, vthlx, vthly);
n = makeXmlPrimitive(t.nodeGroups, tmp + "-Transistor-S", func, 0, 0, 0, 0,
new SizeOffset(shortSox, shortSox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, tmp + "-S");
/*************************************/
// Transistors with extra polys
// different select for those with extra protection layers
nodesList.remove(xTranSelLayer);
double endOfProtectionY = gate_length.value + poly_protection_spacing.value;
double selectExtraY = scaledValue(gate_length.value /2 + endOfProtectionY + extraSelX); // actually is extraSelX because of the poly distance!
xTranSelLayer = (makeXmlNodeLayer(selectx, selectx, selectExtraY, selectExtraY, selectLayer, Poly.Type.FILLED));
nodesList.add(xTranSelLayer);
// not sure which condition to apply. It doesn't apply nwell_overhang_diff due to the extra poly
if (DBMath.isLessThan(welly, selectExtraY))
{
welly = selectExtraY;
soy = scaledValue(endOfProtectionY + extraSelX);
}
if (wellLayer != null)
{
nodesList.remove(xTranWellLayer);
xTranWellLayer = (makeXmlNodeLayer(wellx, wellx, welly, welly, wellLayer, Poly.Type.FILLED));
nodesList.add(xTranWellLayer);
}
if (!horizontalFlag)
{
System.out.println("Not working with !horizontal");
assert(false);
}
portNames.clear();
portNames.add(polyLayer.name);
// bottom or left
Xml.NodeLayer bOrL = (makeXmlNodeLayer(gatex, gatex,
DBMath.round((protectDist + 3*endPolyy)),
-DBMath.round(endPolyy + protectDist),
polyLayer, Poly.Type.FILLED, true, false, -1/*3*/)); // port 3 for left/bottom extra poly lb=left bottom
// Adding left
nodesList.add(bOrL);
n = makeXmlPrimitive(t.nodeGroups, name + "-Transistor-B", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, name + "-B");
// top or right
Xml.NodeLayer tOrR = (makeXmlNodeLayer(gatex, gatex,
-DBMath.round(endPolyy + protectDist),
DBMath.round((protectDist + 3*endPolyy)),
polyLayer, Poly.Type.FILLED, true, false, -1/*4*/)); // port 4 for right/top extra poly rt=right top
// Adding both
nodesList.add(tOrR);
n = makeXmlPrimitive(t.nodeGroups, name + "-Transistor-TB", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, name + "-TB");
// Adding right
nodesList.remove(bOrL);
n = makeXmlPrimitive(t.nodeGroups, name + "-Transistor-T", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, name +"-T");
/*************************************/
// Short transistors woth OD18
double od18x = scaledValue(gate_od18_width.value /2+od18_diff_overhang[0].value);
double od18y = scaledValue(gate_od18_length.value /2+diff_poly_overhang.value +od18_diff_overhang[1].value);
nodePorts.clear();
nodesList.clear();
prepareTransistor(gate_od18_width.value, gate_od18_length.value, poly_endcap.value, diff_poly_overhang.value,
gate_contact_spacing.value, contact_size.value, activeLayer, polyLayer, polyGateLayer, nodesList, nodePorts);
// OD18
addXmlNodeLayer(nodesList, t, "OD_18", od18x, od18y);
// adding short select
shortSelectX = scaledValue(gate_od18_width.value /2+poly_endcap.value);
selecty = scaledValue(gate_od18_length.value /2+diff_poly_overhang.value +extraSelY);
xTranSelLayer = (makeXmlNodeLayer(shortSelectX, shortSelectX, selecty, selecty, selectLayer, Poly.Type.FILLED));
nodesList.add(xTranSelLayer);
// adding well
if (wellLayer != null)
{
xTranWellLayer = (makeXmlNodeLayer(od18x, od18x, od18y, od18y, wellLayer, Poly.Type.FILLED));
nodesList.add(xTranWellLayer);
}
sox = scaledValue(od18_diff_overhang[0].value);
soy = scaledValue(diff_poly_overhang.value +od18_diff_overhang[1].value);
n = makeXmlPrimitive(t.nodeGroups, "OD18-" + name + "-Transistor-S", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, "18-" + name + "-S");
/*************************************/
// Short transistors with OD18ud15
WizardField gateOD18ud1Len = findWizardField("gate_od18ud15_length");
nodePorts.clear();
nodesList.clear();
prepareTransistor(gate_od18_width.value, gateOD18ud1Len.value, poly_endcap.value, diff_poly_overhang.value,
gate_contact_spacing.value, contact_size.value, activeLayer, polyLayer, polyGateLayer, nodesList, nodePorts);
// OD18. od18y is corrected to match gateOD18ud1Len
od18y = scaledValue(gateOD18ud1Len.value /2+diff_poly_overhang.value +od18_diff_overhang[1].value);
addXmlNodeLayer(nodesList, t, "OD_18", od18x, od18y);
// OD18ud15. Not the same as od18 anymore to be more flexible.
WizardRuleFields od18ud15DifOverhang = findWizardRuleFields("od18u15_diff_overhang");
double od18du15x = scaledValue(gate_od18_width.value /2+od18ud15DifOverhang.xValue.value);
double od18du15y = scaledValue(gateOD18ud1Len.value /2+diff_poly_overhang.value +od18ud15DifOverhang.yValue.value);
addXmlNodeLayer(nodesList, t, "OD_18ud15", od18du15x, od18du15y);
// adding short select
shortSelectX = scaledValue(gate_od18_width.value /2+poly_endcap.value);
selecty = scaledValue(gateOD18ud1Len.value /2+diff_poly_overhang.value +extraSelY);
xTranSelLayer = (makeXmlNodeLayer(shortSelectX, shortSelectX, selecty, selecty, selectLayer, Poly.Type.FILLED));
nodesList.add(xTranSelLayer);
// Assuming od18 is bigger than od18ud15
double wellX = od18x;
double wellY = od18y;
double biggerX = od18_diff_overhang[0].value;
double biggerY = od18_diff_overhang[1].value;
// adjusting well and so values if od18ud15 is bigger
if (DBMath.isGreaterThan(od18ud15DifOverhang.xValue.value, od18_diff_overhang[0].value))
{
wellX = od18du15x;
biggerX = od18ud15DifOverhang.xValue.value;
}
if (DBMath.isGreaterThan(od18ud15DifOverhang.yValue.value, od18_diff_overhang[1].value))
{
wellY = od18du15y;
biggerY = od18ud15DifOverhang.yValue.value;
}
// adding well
if (wellLayer != null)
{
xTranWellLayer = (makeXmlNodeLayer(wellX, wellX, wellY, wellY, wellLayer, Poly.Type.FILLED));
nodesList.add(xTranWellLayer);
}
sox = scaledValue(biggerX);
soy = scaledValue(diff_poly_overhang.value + biggerY);
n = makeXmlPrimitive(t.nodeGroups, "OD18ud15-" + name + "-Transistor-S", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, "18ud15-" + name + "-S");
/*************************************/
// Short transistors with native
if (i==Technology.N_TYPE)
{
double ntx = scaledValue(gate_nt_width.value /2+nt_diff_overhang.value);
double nty = scaledValue(gate_nt_length.value /2+diff_poly_overhang.value +nt_diff_overhang.value);
nodePorts.clear();
nodesList.clear();
prepareTransistor(gate_nt_width.value, gate_nt_length.value, poly_nt_endcap.value, diff_poly_overhang.value,
gate_contact_spacing.value, contact_size.value, activeLayer, polyLayer, polyGateLayer, nodesList, nodePorts);
// NT-N
addXmlNodeLayer(nodesList, t, "NT-N", ntx, nty);
// adding short select
shortSelectX = scaledValue(gate_nt_width.value /2+poly_nt_endcap.value);
selecty = scaledValue(gate_nt_length.value /2+diff_poly_overhang.value +extraSelY);
xTranSelLayer = (makeXmlNodeLayer(shortSelectX, shortSelectX, selecty, selecty, selectLayer, Poly.Type.FILLED));
nodesList.add(xTranSelLayer);
// adding well
if (wellLayer != null)
{
xTranWellLayer = (makeXmlNodeLayer(ntx, ntx, nty, nty, wellLayer, Poly.Type.FILLED));
nodesList.add(xTranWellLayer);
}
sox = scaledValue(poly_nt_endcap.value);
soy = scaledValue(diff_poly_overhang.value +nt_diff_overhang.value);
n = makeXmlPrimitive(t.nodeGroups, "NT-" + name + "-Transistor-S", func, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addElement(n, "NT-" + name + "-S");
}
/*************************************/
// Poly Resistors
nodesList.clear();
nodePorts.clear();
WizardField polyRL = findWizardField("poly_resistor_length");
WizardField polyRW = findWizardField("poly_resistor_width");
WizardField rpoS = findWizardField("rpo_contact_spacing");
WizardField rpoODPolyEx = findWizardField("rpo_odpoly_overhang");
WizardField rhOverhang = findWizardField("rh_odpoly_overhang");
double resistorSpacing = contact_array_spacing.value; // using array value to guarantee proper spacing in nD cases
// poly
double soxNoScaled = (rpoS.value + contact_poly_overhang.value + resistorSpacing + 2 * contact_size.value);
double halfTotalL = scaledValue(polyRL.value /2 + soxNoScaled);
double halfTotalW = scaledValue(polyRW.value /2);
nodesList.add(makeXmlNodeLayer(halfTotalL, halfTotalL, halfTotalW, halfTotalW, polyLayer,
Poly.Type.FILLED, true, true, -1));
// RPO
double rpoY = scaledValue(polyRW.value /2 + rpoODPolyEx.value);
double rpoX = scaledValue(polyRL.value /2);
Xml.Layer rpoLayer = t.findLayer("RPO");
addXmlNodeLayerInternal(nodesList, t, "RPO", rpoX, rpoY, true, true, -1);
// left cuts
double cutDistance = scaledValue(rpoS.value + polyRL.value /2);
// M1 and Poly overhang will be the same for now
// double absVal = (contact_poly_overhang.v - via_overhang[0].v);
double m1Distance = cutDistance - scaledValue(contact_poly_overhang.value);
double m1Y = scaledValue(polyRW.value /2); // - absVal);
double m1W = scaledValue(2 * contact_poly_overhang.value + resistorSpacing + 2 * contact_size.value);
double cutSizeHalf = scaledValue(contact_size.value /2);
double cutEnd = cutDistance+contSize;
double cutSpacing = scaledValue(resistorSpacing);
double cutEnd2 = cutEnd+contSize+cutSpacing;
portNames.clear();
portNames.add(m1Layer.name);
// left port
Xml.PrimitivePort port = makeXmlPrimitivePort("left-rpo", 0, 180, 0, minFullSize,
-(cutEnd + cutSpacing), -1, -cutEnd, -1, -cutSizeHalf, -1, cutSizeHalf, 1, portNames);
nodePorts.add(port);
// right port
port = makeXmlPrimitivePort("right-rpo", 0, 180, 1, minFullSize,
cutEnd, 1, (cutEnd + cutSpacing), 1, -cutSizeHalf, -1, cutSizeHalf, 1, portNames);
nodePorts.add(port);
// metal left
nodesList.add(makeXmlNodeLayer((m1Distance + m1W), -1, -m1Distance, -1, m1Y, -1, m1Y, 1, m1Layer,
Poly.Type.FILLED, true, true, 0));
// right metal
nodesList.add(makeXmlNodeLayer(-m1Distance, 1, (m1Distance + m1W), 1, m1Y, -1, m1Y, 1, m1Layer,
Poly.Type.FILLED, true, true, 1));
// select
double selectY = scaledValue(polyRW.value /2 + rhOverhang.value);
double selectX = scaledValue(polyRL.value /2 + soxNoScaled + extraSelX);
nodesList.add(makeXmlNodeLayer(selectX, selectX, selectY, selectY, selectLayer,
Poly.Type.FILLED, true, true, -1));
// RH
addXmlNodeLayerInternal(nodesList, t, "RH", selectX, selectY, true, true, -1);
// RPDMY
addXmlNodeLayerInternal(nodesList, t, "RPDMY", selectX, selectY, true, true, -1);
// cuts
nodesList.add(makeXmlMulticut(cutEnd2, -1, -cutDistance, -1, cutSizeHalf, -1, cutSizeHalf, 1,
polyConLayer, contSize, contArraySpacing, contArraySpacing));
nodesList.add(makeXmlMulticut(-cutDistance, 1, cutEnd2, 1, cutSizeHalf, -1, cutSizeHalf, 1,
polyConLayer, contSize, contArraySpacing, contArraySpacing));
sox = scaledValue(soxNoScaled + extraSelX);
soy = scaledValue(rpoODPolyEx.value);
n = makeXmlPrimitive(t.nodeGroups, name + "-Poly-RPO-Resistor", prFunc, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addPinOrResistor(n, name + "-RPoly");
/*************************************/
// Well Resistors
nodesList.clear();
nodePorts.clear();
WizardField wellRL = findWizardField("well_resistor_length");
WizardField wellRW = findWizardField("well_resistor_width");
WizardField rpoSelO = findWizardField("rpo_select_overlap"); // F
WizardField rpoCoS = findWizardField("rpo_co_space_in_nwrod"); // G
WizardField coNwrodO = findWizardField("co_nwrod_overhang"); // E
WizardField odNwrodO = findWizardField("od_nwrod_overhang"); // D
WizardField rpoNwrodS = findWizardField("rpo_nwrod_space"); // c
// Total values define RPO dimensions
double cutEndNoScaled = /*F*/rpoSelO.value + /*G*/rpoCoS.value;
double cutSpacingNoScaled = /*2xCut + spacing*/resistorSpacing + 2*contact_size.value;
double wellFromnwdmyWidth = /*F+G*/cutEndNoScaled + /*cut spacing+2xcuts*/cutSpacingNoScaled
+ /*E*/coNwrodO.value;
double activeXNoScaled = wellFromnwdmyWidth + /*D*/odNwrodO.value;
soxNoScaled = activeXNoScaled + rpoODPolyEx.value;
double soyNoScaled = /*D*/odNwrodO.value + rpoODPolyEx.value;
halfTotalL = scaledValue(wellRL.value /2 + soxNoScaled);
halfTotalW = scaledValue(wellRW.value /2 + soyNoScaled);
double activeWX = scaledValue(wellRL.value /2 + activeXNoScaled);
double activeWY = scaledValue(wellRW.value /2 + /*D*/odNwrodO.value);
// active
nodesList.add(makeXmlNodeLayer(activeWX, activeWX, activeWY, activeWY, activeLayer,
Poly.Type.FILLED, true, true, -1));
// well
double halfW = scaledValue(wellRW.value /2);
double halfWellLNoScaled = wellRL.value /2 + wellFromnwdmyWidth;
double halfWellL = scaledValue(halfWellLNoScaled);
if (i==Technology.N_TYPE)
{
nodesList.add(makeXmlNodeLayer(halfWellL, halfWellL, halfW, halfW, nwellLayer,
Poly.Type.FILLED, true, true, -1));
}
// NWDMY-LVS
double halfL = scaledValue(wellRL.value /2);
addXmlNodeLayerInternal(nodesList, t, "NWDMY-LVS", halfL, halfTotalW, true, true, -1);
cutEnd = scaledValue(wellRL.value /2+cutEndNoScaled);
cutSpacing = scaledValue(cutSpacingNoScaled);
// Metal1
m1Distance = scaledValue(wellRL.value /2 + /*F*/rpoSelO.value);
// metal left
nodesList.add(makeXmlNodeLayer(halfWellL, -1, -m1Distance, -1, halfW, -1, halfW, 1, m1Layer,
Poly.Type.FILLED, true, true, 0));
// right metal
nodesList.add(makeXmlNodeLayer(-m1Distance, 1, halfWellL, 1, halfW, -1, halfW, 1, m1Layer,
Poly.Type.FILLED, true, true, 1));
// Select
double deltaFromActve = /*DodNwrodO.value - */ /*C*/rpoNwrodS.value + /*F*/rpoSelO.value;
selectY = scaledValue(wellRW.value /2 + deltaFromActve); // Y end of well + F + C value
selectX = scaledValue(halfWellLNoScaled + deltaFromActve); // X end of well + F + CO value
// Left
nodesList.add(makeXmlNodeLayer(selectX, -1, -halfL, -1, selectY, -1, selectY, 1, selectLayer,
Poly.Type.FILLED, true, true, 0));
// right
nodesList.add(makeXmlNodeLayer(-halfL, -1, selectX, 1, selectY, -1, selectY, 1, selectLayer,
Poly.Type.FILLED, true, true, 0));
// m1 left port
port = makeXmlPrimitivePort("left-rpo", 0, 180, 0, minFullSize,
-(cutEnd + cutSpacing), -1, -cutEnd, -1, -halfW, -1, halfW, 1, portNames);
nodePorts.add(port);
// right port
port = makeXmlPrimitivePort("right-rpo", 0, 180, 1, minFullSize,
cutEnd, 1, (cutEnd + cutSpacing), 1, -halfW, -1, halfW, 1, portNames);
nodePorts.add(port);
// RPO in 5 pieces to represent the two holes for the contacts
double holeStartX = scaledValue(halfWellLNoScaled + /*C*/rpoNwrodS.value);
double holeStartY = scaledValue(wellRW.value /2 + /*C*/rpoNwrodS.value);
if (rpoLayer != null)
{
// left piece
nodesList.add(makeXmlNodeLayer(halfTotalL, -1, -holeStartX, -1, halfTotalW, -1, halfTotalW, 1,
rpoLayer, Poly.Type.FILLED, true, true, -1));
// right piece
nodesList.add(makeXmlNodeLayer(-holeStartX, -1, halfTotalL, -1, halfTotalW, -1, halfTotalW, 1,
rpoLayer, Poly.Type.FILLED, true, true, -1));
// center bottom
nodesList.add(makeXmlNodeLayer(holeStartX, -1, holeStartX, 1, halfTotalW, -1, -holeStartY, -1,
rpoLayer, Poly.Type.FILLED, true, true, -1));
// center top
nodesList.add(makeXmlNodeLayer(holeStartX, -1, holeStartX, 1, -holeStartY, -1, halfTotalW, 1,
rpoLayer, Poly.Type.FILLED, true, true, -1));
// center
nodesList.add(makeXmlNodeLayer(m1Distance, m1Distance, holeStartY, holeStartY, rpoLayer,
Poly.Type.FILLED, true, true, -1));
}
else
{
System.out.println("Error: layer rpo doesn't exist");
}
// Cuts
cutEnd2 = cutEnd+cutSpacing;
double cutEndY = scaledValue(wellRW.value /2 - coNwrodO.value); // E should also be applied along Y
// left
nodesList.add(makeXmlMulticut(cutEnd2, -1, -cutEnd, -1, cutEndY, -1, cutEndY, 1,
diffConLayer, contSize, contArraySpacing, contArraySpacing));
// right
nodesList.add(makeXmlMulticut(-cutEnd, 1, cutEnd2, 1, cutEndY, -1, cutEndY, 1,
diffConLayer, contSize, contArraySpacing, contArraySpacing));
sox = scaledValue(soxNoScaled);
soy = scaledValue(soyNoScaled);
n = makeXmlPrimitive(t.nodeGroups, name + "-Well-RPO-Resistor", wrFunc, 0, 0, 0, 0,
new SizeOffset(sox, sox, soy, soy), nodesList, nodePorts, null, false);
g.addPinOrResistor(n, name + "-RWell");
}
}
/*** Palette Elements ***/
allGroups.add(transPalette[0]); allGroups.add(transPalette[1]);
allGroups.add(diffPalette[0]); allGroups.add(diffPalette[1]);
allGroups.add(wellPalette[0]); allGroups.add(wellPalette[1]);
allGroups.addAll(polysGroup);
/*** GDS Values ***/
makeLayerGDS(t, diffPLayer, String.valueOf(diff_layer));
makeLayerGDS(t, diffNLayer, String.valueOf(diff_layer));
makeLayerGDS(t, pplusLayer, String.valueOf(pplus_layer));
makeLayerGDS(t, nplusLayer, String.valueOf(nplus_layer));
makeLayerGDS(t, nwellLayer, String.valueOf(nwell_layer));
makeLayerGDS(t, polyConLayer, String.valueOf(contact_layer));
makeLayerGDS(t, diffConLayer, String.valueOf(contact_layer));
makeLayerGDS(t, polyLayer, String.valueOf(poly_layer));
makeLayerGDS(t, polyGateLayer, String.valueOf(poly_layer));
/*** Layer Rules ***/
for (Xml.Layer l : diffLayers)
{
makeLayerRuleMinRule(t, l, DRCTemplate.DRCRuleType.MINWID, diff_width);
makeLayersRule(t, l, DRCTemplate.DRCRuleType.SPACING, diff_spacing.rule, diff_spacing.value);
}
WizardField[] plus_diff = {pplus_overhang_diff, nplus_overhang_diff};
WizardField[] plus_width = {pplus_width, nplus_width};
WizardField[] plus_spacing = {pplus_spacing, nplus_spacing};
for (int i = 0; i < plusLayers.length; i++)
{
makeLayerRuleMinRule(t, plusLayers[i], DRCTemplate.DRCRuleType.MINWID, plus_width[i]);
makeLayersRuleSurround(t, plusLayers[i], diffLayers[i], plus_diff[i].rule, plus_diff[i].value);
makeLayersRule(t, plusLayers[i], DRCTemplate.DRCRuleType.SPACING, plus_spacing[i].rule, plus_spacing[i].value);
}
Xml.Layer[] wells = {pwellLayer, nwellLayer};
for (Xml.Layer w : wells)
{
makeLayerRuleMinRule(t, w, DRCTemplate.DRCRuleType.MINWID, nwell_width);
makeLayersRuleSurround(t, w, diffPLayer, nwell_overhang_diff_p.rule, nwell_overhang_diff_p.value);
makeLayersRuleSurround(t, w, diffNLayer, nwell_overhang_diff_n.rule, nwell_overhang_diff_n.value);
makeLayersRule(t, w, DRCTemplate.DRCRuleType.SPACING, nwell_spacing.rule, nwell_spacing.value);
}
Xml.Layer[] polys = {polyLayer, polyGateLayer};
for (Xml.Layer w : polys)
{
makeLayerRuleMinRule(t, w, DRCTemplate.DRCRuleType.MINWID, poly_width);
makeLayersRule(t, w, DRCTemplate.DRCRuleType.SPACING, poly_spacing.rule, poly_spacing.value);
}
// Gate contact spacing rules
makeLayersRuleSpacing(t, diffConLayer, polyGateLayer, gate_contact_spacing.rule, gate_contact_spacing.value);
makeLayersRuleSpacing(t, polyConLayer, polyGateLayer, gate_contact_spacing.rule, gate_contact_spacing.value);
}
private void addContactsOrCapacitors(Xml.Technology t, List<Contact> contacts, List<Xml.Layer> metalLayers,
PaletteGroup[] diffPalette, PaletteGroup[] wellPalette,
PaletteGroup polyGroup, boolean capacitor)
{
List<String> portArcNames = new ArrayList<String>(0);
List<String> portNames = new ArrayList<String>(2);
// Typical port names in capacitors
portNames.add("a");
portNames.add("b");
// generic contacts
String name = null;
Xml.Layer polyLayer = t.findLayer(poly_layer.name);
Xml.Layer polyConLayer = t.findLayer("Poly-Cut");
Xml.Layer diffConLayer = t.findLayer(diff_layer.name+"-Cut");
Xml.Layer nwellLayer = t.findLayer(nwell_layer.name);
Xml.Layer pwellLayer = t.findLayer("P-Well");
Xml.Layer diffNLayer = t.findLayer("N-"+ diff_layer.name);
Xml.Layer diffPLayer = t.findLayer("P-"+ diff_layer.name);
Xml.Layer[] diffLayers = {diffPLayer, diffNLayer};
Xml.Layer pol2yLayer = t.findLayer(poly2_layer.name);
double contSize = scaledValue(contact_size.value);
double contSpacing = scaledValue(contact_spacing.value);
double contArraySpacing = scaledValue(contact_array_spacing.value);
for (Contact c : contacts)
{
Xml.Layer ly = null, lx = null;
Xml.Layer conLay = diffConLayer;
PaletteGroup g = null;
RectLayerNode metalLayer = (RectLayerNode)c.layers.get(0);
RectLayerNode otherLayer = (RectLayerNode)c.layers.get(1);
String extraName = "";
if (!TextUtils.isANumber(metalLayer.layer)) // horizontal must be!
{
assert (TextUtils.isANumber(otherLayer.layer));
metalLayer = (RectLayerNode)c.layers.get(1);
otherLayer = (RectLayerNode)c.layers.get(0);
}
int m1 = Integer.valueOf(metalLayer.layer);
ly = metalLayers.get(m1-1);
String layerName = otherLayer.layer;
if (layerName.equals(diffLayers[0].name))
{
lx = diffLayers[0];
g = diffPalette[0];
extraName = "P";
}
else if (layerName.equals(diffLayers[1].name))
{
lx = diffLayers[1];
g = diffPalette[1];
extraName = "N";
}
else if (layerName.equals(polyLayer.name))
{
lx = polyLayer;
conLay = polyConLayer;
g = polyGroup;
}
else if (getSecondPolyFlag() && layerName.equals(pol2yLayer.name))
{
lx = pol2yLayer;
conLay = polyConLayer;
g = polyGroup;
}
else
assert(false); // it should not happen
double h1x = scaledValue(contact_size.value /2 + metalLayer.valueX.value);
double h1y = scaledValue(contact_size.value /2 + metalLayer.valueY.value);
double h2x = scaledValue(contact_size.value /2 + otherLayer.valueX.value);
double h2y = scaledValue(contact_size.value /2 + otherLayer.valueY.value);
double longX = (Math.abs(metalLayer.valueX.value - otherLayer.valueX.value));
double longY = (Math.abs(metalLayer.valueY.value - otherLayer.valueY.value));
PrimitiveNode.Function func = (!capacitor) ? PrimitiveNode.Function.CONTACT :
PrimitiveNode.Function.CAPAC;
Xml.NodeLayer[] nodes = new Xml.NodeLayer[c.layers.size() + 1]; // all plus cut
int count = 0;
// cut
nodes[count++] = makeXmlMulticut(conLay, contSize, contSpacing, contArraySpacing);
// metal
nodes[count++] = makeXmlNodeLayer(h1x, h1x, h1y, h1y, ly, Poly.Type.FILLED); // layer1
// active or poly
nodes[count++] = makeXmlNodeLayer(h2x, h2x, h2y, h2y, lx, Poly.Type.FILLED); // layer2
Xml.Layer otherLayerPort = lx;
for (int i = 2; i < c.layers.size(); i++) // rest of layers. Either select or well.
{
RectLayerNode node = (RectLayerNode)c.layers.get(i);
Xml.Layer lz = t.findLayer(node.layer);
if ((lz == pwellLayer && lx == diffLayers[0]) ||
(lz == nwellLayer && lx == diffLayers[1])) // well contact
{
otherLayerPort = lz;
if (lz == pwellLayer)
{
g = wellPalette[0];
func = getWellContactFunction(Technology.P_TYPE);
extraName = "PW"; // W for well
}
else // nwell
{
g = wellPalette[1];
func = getWellContactFunction(Technology.N_TYPE);
extraName = "NW"; // W for well
}
}
if (pSubstrateProcess && lz == pwellLayer)
continue; // skip this layer
double h3x = scaledValue(contact_size.value /2 + node.valueX.value);
double h3y = scaledValue(contact_size.value /2 + node.valueY.value);
nodes[count++] = makeXmlNodeLayer(h3x, h3x, h3y, h3y, lz, Poly.Type.FILLED);
// This assumes no well is defined
double longXLocal = (Math.abs(node.valueX.value - otherLayer.valueX.value));
double longYLocal = (Math.abs(node.valueY.value - otherLayer.valueY.value));
if (DBMath.isGreaterThan(longXLocal, longX))
longX = longXLocal;
if (DBMath.isGreaterThan(longYLocal, longY))
longY = longYLocal;
}
longX = scaledValue(longX);
longY = scaledValue(longY);
// arc prt names now after determing wheter is a diff or well contact
portArcNames.clear();
// if (!pSubstrateProcess || otherLayerPort == pwellLayer)
portArcNames.add(otherLayerPort.name);
portArcNames.add(ly.name); // always should represent the metal1
name = ly.name + "-" + otherLayerPort.name;
// some primitives might not have prefix. "-" should not be in the prefix to avoid
// being displayed in the palette
String p = (c.prefix == null || c.prefix.equals("")) ? "" : c.prefix + "-";
Xml.PrimitiveNodeGroup png = (!capacitor) ?
makeXmlPrimitiveCon(t.nodeGroups, p + name, func, -1, -1,
new SizeOffset(longX, longX, longY, longY), portArcNames, nodes) : // contact
makeXmlCapacitor(t.nodeGroups, p + name, func, -1, -1,
new SizeOffset(longX, longX, longY, longY), portNames, portArcNames, nodes); // capacitor
g.addElement(png, p + extraName);
}
}
/***************************************************************************************************
* More Flexible Contacts, no multicuts
***************************************************************************************************/
private void addGenericContacts(Xml.Technology t, Map<String,List<Contact>> contacts, List<PaletteGroup> extraPaletteList)
{
List<String> portNames = new ArrayList<String>(0);
for (Map.Entry<String,List<Contact>> e : contacts.entrySet())
{
// generic contacts
for (Contact c : e.getValue())
{
// Assuming is that the last layer is the cut layer
assert(c.layers.size() == 3);
RectLayerNode aLayer = (RectLayerNode)c.layers.get(0);
RectLayerNode bLayer = (RectLayerNode)c.layers.get(1);
RectLayerNode cutLayer = (RectLayerNode)c.layers.get(2);
// Look for existing palette elemnent to place the contact in.
PaletteGroup grp = null;
for (LayerNode l : c.layers)
{
RectLayerNode n = (RectLayerNode)l;
for (LayerInfo info : extraLayers)
{
if (info.name.equals(n.layer))
{
grp = info.grp; break; // found
}
if (grp != null) break; // found
}
if (grp != null) break; // found
}
if (grp == null)
{
grp = new PaletteGroup();
extraPaletteList.add(grp);
}
Xml.Layer la = t.findLayer(aLayer.layer);
Xml.Layer lb = t.findLayer(bLayer.layer);
String name = la.name + "-" + lb.name;
double metalContSizeX = scaledValue(cutLayer.valueX.value/2);
double metalContSizeY = scaledValue(cutLayer.valueY.value/2);
Xml.Layer metalConLayer = t.findLayer(cutLayer.layer);
double h1x = scaledValue(cutLayer.valueX.value /2 + aLayer.valueX.value);
double h1y = scaledValue(cutLayer.valueY.value /2 + aLayer.valueY.value);
double h2x = scaledValue(cutLayer.valueX.value /2 + bLayer.valueX.value);
double h2y = scaledValue(cutLayer.valueY.value /2 + bLayer.valueY.value);
double longX = scaledValue(Math.abs(aLayer.valueX.value - bLayer.valueX.value));
double longY = scaledValue(Math.abs(aLayer.valueY.value - bLayer.valueY.value));
portNames.clear();
// only when it is zero or positive. Negative means no layer arc
if (bLayer.valueX.value >= 0)
portNames.add(lb.name);
if (aLayer.valueX.value >= 0)
portNames.add(la.name);
// some primitives might not have prefix. "-" should not be in the prefix to avoid
// being displayed in the palette
String p = (c.prefix == null || c.prefix.equals("")) ? "" : c.prefix + "-";
grp.addElement(makeXmlPrimitiveCon(t.nodeGroups, p + name, PrimitiveNode.Function.CONTACT, -1, -1,
/*new SizeOffset(longX, longX, longY, longY)*/null,
portNames,
makeXmlNodeLayer(h1x, h1x, h1y, h1y, la, Poly.Type.FILLED), // layer1
makeXmlNodeLayer(h2x, h2x, h2y, h2y, lb, Poly.Type.FILLED), // layer2
makeXmlNodeLayer(metalContSizeX, metalContSizeX, metalContSizeY, metalContSizeY,
metalConLayer, Poly.Type.FILLED)), // cut
c.prefix); // contact
}
}
}
/***************************************************************************************************
* PrimitiveNodeGroup Comparator
***************************************************************************************************/
/**
* A comparator object for sorting NodeGroups
* Created once because it is used often.
*/
private static final PrimitiveNodeGroupSort primitiveNodeGroupSort = new PrimitiveNodeGroupSort();
/**
* Comparator class for sorting PrimitiveNodeGroups by their name.
*/
public static class PrimitiveNodeGroupSort implements Comparator<Xml.PrimitiveNodeGroup>
{
/**
* Method to compare two PrimitiveNodeGroups by their name.
* @param l1 one PrimitiveNodeGroup.
* @param l2 another PrimitiveNodeGroup.
* @return an integer indicating their sorting order.
*/
public int compare(Xml.PrimitiveNodeGroup l1, Xml.PrimitiveNodeGroup l2)
{
// Sorting by first element
Xml.PrimitiveNode n1 = l1.nodes.get(0);
Xml.PrimitiveNode n2 = l2.nodes.get(0);
return n1.name.compareTo(n2.name);
}
}
/***************************************************************************************************
* NodeLayer Comparator
***************************************************************************************************/
/**
* A comparator object for sorting NodeLayers
* Created once because it is used often.
*/
private static final NodeLayerSort nodeLayerSort = new NodeLayerSort();
/**
* Comparator class for sorting PrimitiveNodeGroups by their name.
*/
public static class NodeLayerSort implements Comparator<Xml.NodeLayer>
{
/**
* Method to compare two NodeLayers by their name.
* @param l1 one NodeLayer.
* @param l2 another NodeLayer.
* @return an integer indicating their sorting order.
*/
public int compare(Xml.NodeLayer l1, Xml.NodeLayer l2)
{
return l1.layer.compareTo(l2.layer);
}
}
}