/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LibToTech.java
* Technology Editor, conversion of technology libraries to technologies
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2005, 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.tecEdit;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.DRCTemplate;
import com.sun.electric.technology.EdgeH;
import com.sun.electric.technology.EdgeV;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.SizeOffset;
import com.sun.electric.technology.TechFactory;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.Xml;
import com.sun.electric.technology.ArcProto.Function;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
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.User;
import com.sun.electric.tool.user.dialogs.EDialog;
import com.sun.electric.tool.user.dialogs.OpenFile;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.net.URL;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* This class creates technologies from technology libraries.
*/
public class LibToTech
{
/* the meaning of "us_tecflags" */
// private static final int HASDRCMINWID = 01; /* has DRC minimum width information */
// private static final int HASDRCMINWIDR = 02; /* has DRC minimum width information */
// private static final int HASCOLORMAP = 04; /* has color map */
// private static final int HASARCWID = 010; /* has arc width offset factors */
// private static final int HASCIF = 020; /* has CIF layers */
// private static final int HASDXF = 040; /* has DXF layers */
// private static final int HASGDS = 0100; /* has Calma GDS-II layers */
// private static final int HASGRAB = 0200; /* has grab point information */
// private static final int HASSPIRES = 0400; /* has SPICE resistance information */
// private static final int HASSPICAP = 01000; /* has SPICE capacitance information */
// private static final int HASSPIECAP = 02000; /* has SPICE edge capacitance information */
// private static final int HAS3DINFO = 04000; /* has 3D height/thickness information */
// private static final int HASCONDRC = 010000; /* has connected design rules */
// private static final int HASCONDRCR = 020000; /* has connected design rules reasons */
// private static final int HASUNCONDRC = 040000; /* has unconnected design rules */
// private static final int HASUNCONDRCR = 0100000; /* has unconnected design rules reasons */
// private static final int HASCONDRCW = 0200000; /* has connected wide design rules */
// private static final int HASCONDRCWR = 0400000; /* has connected wide design rules reasons */
// private static final int HASUNCONDRCW = 01000000; /* has unconnected wide design rules */
// private static final int HASUNCONDRCWR = 02000000; /* has unconnected wide design rules reasons */
// private static final int HASCONDRCM = 04000000; /* has connected multicut design rules */
// private static final int HASCONDRCMR = 010000000; /* has connected multicut design rules reasons */
// private static final int HASUNCONDRCM = 020000000; /* has unconnected multicut design rules */
// private static final int HASUNCONDRCMR = 040000000; /* has unconnected multicut design rules reasons */
// private static final int HASEDGEDRC = 0100000000; /* has edge design rules */
// private static final int HASEDGEDRCR = 0200000000; /* has edge design rules reasons */
// private static final int HASMINNODE = 0400000000; /* has minimum node size */
// private static final int HASMINNODER = 01000000000; /* has minimum node size reasons */
// private static final int HASPRINTCOL = 02000000000; /* has print colors */
/* the globals that define a technology */
// static int us_tecflags;
// static DRCRULES *us_tecdrc_rules = 0;
private TechConversionResult error;
/************************************* API AND USER INTERFACE *************************************/
/**
* Method to convert the current library to a technology in a new job.
* Starts with a dialog to control the process.
*/
public static void makeTechFromLib()
{
new GenerateTechnology();
}
/**
* This class displays a dialog for converting a library to a technology.
*/
private static class GenerateTechnology extends EDialog
{
private JLabel lab2, lab3;
private JTextField renameName, newName;
private JCheckBox alsoXML;
/** Creates new form convert library to technology */
private GenerateTechnology()
{
super(null, true);
initComponents();
nameChanged();
setVisible(true);
}
protected void escapePressed() { exit(false); }
// Call this method when the user clicks the OK button
private void exit(boolean goodButton)
{
if (goodButton)
new TechFromLibJob(Library.getCurrent(), newName.getText(), alsoXML.isSelected());
dispose();
}
private void nameChanged()
{
String techName = newName.getText();
if (Technology.findTechnology(techName) != null)
{
// name exists, offer to rename it
lab2.setEnabled(true);
lab3.setEnabled(true);
renameName.setEnabled(true);
renameName.setEditable(true);
} else
{
// name is unique, don't offer to rename it
lab2.setEnabled(false);
lab3.setEnabled(false);
renameName.setEnabled(false);
renameName.setEditable(false);
}
}
private void initComponents()
{
getContentPane().setLayout(new GridBagLayout());
setTitle("Convert Library to Technology");
setName("");
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent evt) { exit(false); }
});
JLabel lab1 = new JLabel("Creating new technology:");
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab1, gbc);
newName = new JTextField(Library.getCurrent().getName());
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(newName, gbc);
newName.getDocument().addDocumentListener(new TechNameDocumentListener());
lab2 = new JLabel("Already a technology with this name");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 3;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab2, gbc);
lab3 = new JLabel("Rename existing technology to:");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 2;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab3, gbc);
renameName = new JTextField();
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 2;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(renameName, gbc);
alsoXML = new JCheckBox("Also write XML code");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 3;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(alsoXML, gbc);
// OK and Cancel
JButton cancel = new JButton("Cancel");
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 3;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(cancel, gbc);
cancel.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { exit(false); }
});
JButton ok = new JButton("OK");
getRootPane().setDefaultButton(ok);
gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 3;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(ok, gbc);
ok.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { exit(true); }
});
pack();
}
/**
* Class to handle special changes to the new technology name.
*/
private class TechNameDocumentListener implements DocumentListener
{
public void changedUpdate(DocumentEvent e) { nameChanged(); }
public void insertUpdate(DocumentEvent e) { nameChanged(); }
public void removeUpdate(DocumentEvent e) { nameChanged(); }
}
}
/************************************* BUILDING TECHNOLOGY FROM LIBRARY *************************************/
/**
* Class to create a technology-library from a technology (in a Job).
*/
private static class TechFromLibJob extends Job
{
private Library techLib;
private String newName;
private String fileName;
private TechConversionResult tcr;
private TechFromLibJob(Library techLib, String newName, boolean alsoXML)
{
super("Make Technology from Technology Library", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.techLib = techLib;
this.newName = newName;
if (alsoXML)
{
// print the technology as XML
fileName = OpenFile.chooseOutputFile(FileType.XML, "File for Technology's XML Code",
newName + ".xml");
}
startJob();
}
@Override
public boolean doIt()
{
LibToTech ltt = new LibToTech();
tcr = new TechConversionResult();
ltt.makeTech(techLib, newName, fileName, tcr);
fieldVariableChanged("tcr");
return true;
}
public void terminateOK()
{
if (tcr.failed())
{
tcr.showError();
System.out.println("Failed to convert the library to a technology");
}
}
}
/**
* Method to convert the current Library to a Technology.
* @param lib technology Library
* @param newName the name of the Technology to create.
* @param fileName the name of the XML file to write (null to skip XML output).
* @param error the structure for storing error status.
* @return the new Technology. Returns null on error (and fills in "error").
*/
public Technology makeTech(Library lib, String newName, String fileName, TechConversionResult error)
{
this.error = error;
// get a new name for the technology
String newTechName = newName;
boolean modified = false;
for(;;)
{
// search by hand because "gettechnology" handles partial matches
if (Technology.findTechnology(newTechName) == null) break;
newTechName += "X";
modified = true;
}
if (modified)
System.out.println("Warning: already a technology called " + newName + ". Naming this " + newTechName);
// get list of dependent libraries
Library [] dependentLibs = Info.getDependentLibraries(lib);
// get general information from the "factors" cell
Cell np = null;
for(int i=dependentLibs.length-1; i>=0; i--)
{
np = dependentLibs[i].findNodeProto("factors");
if (np != null) break;
}
if (np == null)
{
error.markError(null, null, "Cell with general information, called 'factors', is missing");
return null;
}
GeneralInfo gi = parseCell(np);
// get layer information
LayerInfo [] lList = extractLayers(dependentLibs);
if (lList == null) return null;
// get arc information
ArcInfo [] aList = extractArcs(dependentLibs, lList);
if (aList == null) return null;
// get node information
NodeInfo [] nList = extractNodes(dependentLibs, lList, aList);
if (nList == null) return null;
for(NodeInfo ni: nList)
ni.arcsShrink = ni.func.isPin() && !ni.wipes;
// create the pure-layer associations
for(int i=0; i<lList.length; i++)
{
if (lList[i].pseudo) continue;
// find the pure layer node
for(int j=0; j<nList.length; j++)
{
if (nList[j].func != PrimitiveNode.Function.NODE) continue;
NodeInfo.LayerDetails nld = nList[j].nodeLayers[0];
if (nld.layer == lList[i])
{
lList[i].pureLayerNode = nList[j];
break;
}
}
}
// add the component menu information if available
Variable var = Library.getCurrent().getVar(Info.COMPMENU_KEY);
if (var != null)
{
String compMenuXML = (String)var.getObject();
List<Xml.PrimitiveNodeGroup> nodeGroups = new ArrayList<Xml.PrimitiveNodeGroup>();
for(int i=0; i<nList.length; i++)
{
Xml.PrimitiveNodeGroup xng = new Xml.PrimitiveNodeGroup();
Xml.PrimitiveNode xnp = new Xml.PrimitiveNode();
xnp.name = nList[i].name;
xnp.function = nList[i].func;
xng.nodes.add(xnp);
nodeGroups.add(xng);
}
List<Xml.ArcProto> arcs = new ArrayList<Xml.ArcProto>();
for(int i=0; i<aList.length; i++)
{
Xml.ArcProto xap = new Xml.ArcProto();
xap.name = aList[i].name;
arcs.add(xap);
}
List<Xml.PrimitiveNode> pureLayerNodes = new ArrayList<Xml.PrimitiveNode>();
for(int i=0; i<lList.length; i++)
{
Xml.PrimitiveNode pln = new Xml.PrimitiveNode();
pln.name = "node-" + lList[i].name;
pureLayerNodes.add(pln);
}
gi.menuPalette = Xml.parseComponentMenuXMLTechEdit(compMenuXML, nodeGroups, arcs, pureLayerNodes);
}
Xml.Technology t = makeXml(newTechName, gi, lList, nList, aList);
URL techUrl = null;
if (fileName != null) {
boolean includeDateAndVersion = User.isIncludeDateAndVersionInOutput();
String copyrightMessage = IOTool.isUseCopyrightMessage() ? IOTool.getCopyrightMessage() : null;
t.writeXml(fileName, includeDateAndVersion, copyrightMessage);
techUrl = TextUtils.makeURLToFile(fileName);
}
TechFactory techFactory = TechFactory.fromXml(techUrl, t);
Technology tech = techFactory.newInstance(Generic.tech());
if (tech == null) System.out.println("ERROR creating new technology"); else
{
lib.getDatabase().addTech(tech);
// switch to this technology
System.out.println("Technology " + tech.getTechName() + " built.");
}
return tech;
}
// private void checkAndWarn(LayerInfo [] lList, ArcInfo [] aList, NodeInfo [] nList)
// {
// // make sure there is a pure-layer node for every nonpseudo layer
// for(int i=0; i<lList.length; i++)
// {
// if (lList[i].pseudo) continue;
// boolean found = false;
// for(int j=0; j<nList.length; j++)
// {
// NodeInfo nIn = nList[j];
// if (nIn.func != PrimitiveNode.Function.NODE) continue;
// if (nIn.nodeLayers[0].layer == lList[i])
// {
// found = true;
// break;
// }
// }
// if (found) continue;
// System.out.println("Warning: Layer " + lList[i].name + " has no associated pure-layer node");
// }
//
// // make sure there is a pin for every arc and that it uses pseudo-layers
// for(int i=0; i<aList.length; i++)
// {
// // find that arc's pin
// boolean found = false;
// for(int j=0; j<nList.length; j++)
// {
// NodeInfo nIn = nList[j];
// if (nIn.func != PrimitiveNode.Function.PIN) continue;
//
// for(int k=0; k<nIn.nodePortDetails.length; k++)
// {
// ArcInfo [] connections = nIn.nodePortDetails[k].connections;
// for(int l=0; l<connections.length; l++)
// {
// if (connections[l] == aList[i])
// {
// // pin found: make sure it uses pseudo-layers
// boolean allPseudo = true;
// for(int m=0; m<nIn.nodeLayers.length; m++)
// {
// LayerInfo lin = nIn.nodeLayers[m].layer;
// if (!lin.pseudo) { allPseudo = false; break; }
// }
// if (!allPseudo)
// System.out.println("Warning: Pin " + nIn.name + " is not composed of pseudo-layers");
//
// found = true;
// break;
// }
// }
// if (found) break;
// }
// if (found) break;
// }
// if (!found)
// System.out.println("Warning: Arc " + aList[i].name + " has no associated pin node");
// }
// }
/**
* Method to parse the miscellaneous-info cell in "np" and return a GeneralInfo object that describes it.
*/
private GeneralInfo parseCell(Cell np)
{
// create and initialize the GRAPHICS structure
GeneralInfo gi = new GeneralInfo();
for(Iterator<NodeInst> it = np.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
int opt = Manipulate.getOptionOnNode(ni);
String str = Info.getValueOnNode(ni);
switch (opt)
{
case Info.TECHSHORTNAME:
gi.shortName = str;
break;
case Info.TECHSCALE:
gi.scale = TextUtils.atof(str);
gi.scaleRelevant = true;
break;
case Info.TECHRESOLUTION:
gi.resolution = TextUtils.atof(str);
break;
case Info.TECHFOUNDRY:
gi.defaultFoundry = str;
break;
case Info.TECHDEFMETALS:
gi.defaultNumMetals = TextUtils.atoi(str);
break;
case Info.TECHDESCRIPT:
gi.description = str;
break;
case Info.TECHSPICEMINRES:
gi.minRes = TextUtils.atof(str);
break;
case Info.TECHSPICEMINCAP:
gi.minCap = TextUtils.atof(str);
break;
case Info.TECHMAXSERIESRES:
gi.maxSeriesResistance = TextUtils.atof(str);
break;
case Info.TECHGATESHRINK:
gi.gateShrinkage = TextUtils.atof(str);
break;
case Info.TECHGATEINCLUDED:
gi.includeGateInResistance = str.equalsIgnoreCase("yes");
break;
case Info.TECHGROUNDINCLUDED:
gi.includeGround = str.equalsIgnoreCase("yes");
break;
case Info.TECHTRANSPCOLORS:
Color [] colors = GeneralInfo.getTransparentColors(ni);
if (colors != null) gi.transparentColors = colors;
break;
case Info.CENTEROBJ:
break;
default:
error.markError(ni, np, "Unknown node in miscellaneous-information cell");
break;
}
}
return gi;
}
/************************************* LAYER ANALYSIS *************************************/
/**
* Method to scan the "dependentlibcount" libraries in "dependentLibs",
* and build the layer structures for it in technology "tech". Returns true on error.
*/
private LayerInfo [] extractLayers(Library [] dependentLibs)
{
// first find the number of layers
Cell [] layerCells = Info.findCellSequence(dependentLibs, "layer-", Info.LAYERSEQUENCE_KEY);
if (layerCells.length <= 0)
{
System.out.println("No layers found");
return null;
}
// create the layers
LayerInfo [] lis = new LayerInfo[layerCells.length];
for(int i=0; i<layerCells.length; i++)
{
lis[i] = LayerInfo.parseCell(layerCells[i]);
if (lis[i] == null)
{
error.markError(null, layerCells[i], "Error parsing layer information");
continue;
}
}
// // get the design rules
// drcsize = us_teclayer_count*us_teclayer_count/2 + (us_teclayer_count+1)/2;
// us_tecedgetlayernamelist();
// if (us_tecdrc_rules != 0)
// {
// dr_freerules(us_tecdrc_rules);
// us_tecdrc_rules = 0;
// }
// nodecount = Info.findCellSequence(dependentLibs, "node-", Generate.NODERSEQUENCE_KEY);
// us_tecdrc_rules = dr_allocaterules(us_teceddrclayers, nodecount, x_("EDITED TECHNOLOGY"));
// if (us_tecdrc_rules == NODRCRULES) return(TRUE);
// for(i=0; i<us_teceddrclayers; i++)
// (void)allocstring(&us_tecdrc_rules.layernames[i], us_teceddrclayernames[i], el_tempcluster);
// for(i=0; i<nodecount; i++)
// (void)allocstring(&us_tecdrc_rules.nodenames[i], &nodesequence[i].protoname[5], el_tempcluster);
// if (nodecount > 0) efree((CHAR *)nodesequence);
// var = NOVARIABLE;
// for(i=dependentlibcount-1; i>=0; i--)
// {
// var = getval((INTBIG)dependentLibs[i], VLIBRARY, VSTRING|VISARRAY, x_("EDTEC_DRC"));
// if (var != NOVARIABLE) break;
// }
// us_teceditgetdrcarrays(var, us_tecdrc_rules);
// // see which design rules exist
// for(i=0; i<us_teceddrclayers; i++)
// {
// if (us_tecdrc_rules.minwidth[i] >= 0) us_tecflags |= HASDRCMINWID;
// if (*us_tecdrc_rules.minwidthR[i] != 0) us_tecflags |= HASDRCMINWIDR;
// }
// for(i=0; i<drcsize; i++)
// {
// if (us_tecdrc_rules.conlist[i] >= 0) us_tecflags |= HASCONDRC;
// if (*us_tecdrc_rules.conlistR[i] != 0) us_tecflags |= HASCONDRCR;
// if (us_tecdrc_rules.unconlist[i] >= 0) us_tecflags |= HASUNCONDRC;
// if (*us_tecdrc_rules.unconlistR[i] != 0) us_tecflags |= HASUNCONDRCR;
// if (us_tecdrc_rules.conlistW[i] >= 0) us_tecflags |= HASCONDRCW;
// if (*us_tecdrc_rules.conlistWR[i] != 0) us_tecflags |= HASCONDRCWR;
// if (us_tecdrc_rules.unconlistW[i] >= 0) us_tecflags |= HASUNCONDRCW;
// if (*us_tecdrc_rules.unconlistWR[i] != 0) us_tecflags |= HASUNCONDRCWR;
// if (us_tecdrc_rules.conlistM[i] >= 0) us_tecflags |= HASCONDRCM;
// if (*us_tecdrc_rules.conlistMR[i] != 0) us_tecflags |= HASCONDRCMR;
// if (us_tecdrc_rules.unconlistM[i] >= 0) us_tecflags |= HASUNCONDRCM;
// if (*us_tecdrc_rules.unconlistMR[i] != 0) us_tecflags |= HASUNCONDRCMR;
// if (us_tecdrc_rules.edgelist[i] >= 0) us_tecflags |= HASEDGEDRC;
// if (*us_tecdrc_rules.edgelistR[i] != 0) us_tecflags |= HASEDGEDRCR;
// }
// for(i=0; i<us_tecdrc_rules.numnodes; i++)
// {
// if (us_tecdrc_rules.minnodesize[i*2] > 0 ||
// us_tecdrc_rules.minnodesize[i*2+1] > 0) us_tecflags |= HASMINNODE;
// if (*us_tecdrc_rules.minnodesizeR[i] != 0) us_tecflags |= HASMINNODER;
// }
// // store this information on the technology object
// if ((us_tecflags&(HASCONDRCW|HASUNCONDRCW)) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_wide_limitkey, us_tecdrc_rules.widelimit,
// VFRACT|VDONTSAVE);
// if ((us_tecflags&HASDRCMINWID) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_min_widthkey, (INTBIG)us_tecdrc_rules.minwidth,
// VFRACT|VDONTSAVE|VISARRAY|(tech.layercount<<VLENGTHSH));
// if ((us_tecflags&HASDRCMINWIDR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_min_width_rulekey, (INTBIG)us_tecdrc_rules.minwidthR,
// VSTRING|VDONTSAVE|VISARRAY|(tech.layercount<<VLENGTHSH));
// if ((us_tecflags&HASCONDRC) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_connected_distanceskey,
// (INTBIG)us_tecdrc_rules.conlist, VFRACT|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASCONDRCR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_connected_distances_rulekey,
// (INTBIG)us_tecdrc_rules.conlistR, VSTRING|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASUNCONDRC) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_unconnected_distanceskey,
// (INTBIG)us_tecdrc_rules.unconlist, VFRACT|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASUNCONDRCR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_unconnected_distances_rulekey,
// (INTBIG)us_tecdrc_rules.unconlistR, VSTRING|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASCONDRCW) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_connected_distancesWkey,
// (INTBIG)us_tecdrc_rules.conlistW, VFRACT|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASCONDRCWR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_connected_distancesW_rulekey,
// (INTBIG)us_tecdrc_rules.conlistWR, VSTRING|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASUNCONDRCW) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_unconnected_distancesWkey,
// (INTBIG)us_tecdrc_rules.unconlistW, VFRACT|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASUNCONDRCWR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_unconnected_distancesW_rulekey,
// (INTBIG)us_tecdrc_rules.unconlistWR, VSTRING|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASCONDRCM) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_connected_distancesMkey,
// (INTBIG)us_tecdrc_rules.conlistM, VFRACT|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASCONDRCMR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_connected_distancesM_rulekey,
// (INTBIG)us_tecdrc_rules.conlistMR, VSTRING|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASUNCONDRCM) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_unconnected_distancesMkey,
// (INTBIG)us_tecdrc_rules.unconlistM, VFRACT|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASUNCONDRCMR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_unconnected_distancesM_rulekey,
// (INTBIG)us_tecdrc_rules.unconlistMR, VSTRING|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASEDGEDRC) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_edge_distanceskey,
// (INTBIG)us_tecdrc_rules.edgelist, VFRACT|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASEDGEDRCR) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_edge_distances_rulekey,
// (INTBIG)us_tecdrc_rules.edgelistR, VSTRING|VDONTSAVE|VISARRAY|(drcsize<<VLENGTHSH));
// if ((us_tecflags&HASMINNODE) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_min_node_sizekey,
// (INTBIG)us_tecdrc_rules.minnodesize, VFRACT|VDONTSAVE|VISARRAY|((us_tecdrc_rules.numnodes*2)<<VLENGTHSH));
// if ((us_tecflags&HASMINNODER) != 0)
// (void)setvalkey((INTBIG)tech, VTECHNOLOGY, dr_min_node_size_rulekey,
// (INTBIG)us_tecdrc_rules.minnodesizeR, VSTRING|VDONTSAVE|VISARRAY|(us_tecdrc_rules.numnodes<<VLENGTHSH));
return lis;
}
/************************************* ARC ANALYSIS *************************************/
/**
* Method to scan the "dependentlibcount" libraries in "dependentLibs",
* and build the arc structures for it in technology "tech". Returns true on error.
*/
private ArcInfo [] extractArcs(Library [] dependentLibs, LayerInfo [] lList)
{
// count the number of arcs in the technology
Cell [] arcCells = Info.findCellSequence(dependentLibs, "arc-", Info.ARCSEQUENCE_KEY);
if (arcCells.length <= 0)
{
System.out.println("No arcs found");
return null;
}
ArcInfo [] allArcs = new ArcInfo[arcCells.length];
for(int i=0; i<arcCells.length; i++)
{
Cell np = arcCells[i];
allArcs[i] = ArcInfo.parseCell(np);
// build a list of examples found in this arc
List<Example> neList = Example.getExamples(np, false, error, null);
if (neList == null) return null;
if (neList.size() > 1)
{
error.markError(null, np, "Can only be one drawing of an arc, but more were found");
return null;
}
Example arcEx = neList.get(0);
// sort the layers in the example
Collections.sort(arcEx.samples, new SamplesByLayerOrder(lList));
// get width and polygon count information
double hWid = -1;
int count = 0;
for(Sample ns : arcEx.samples)
{
double wid = Math.min(ns.node.getXSize(), ns.node.getYSize());
if (ns.layer == null) hWid = wid; else count++;
}
// error if there is no highlight box
if (hWid < 0)
{
error.markError(null, np, "No highlight layer found");
return null;
}
allArcs[i].arcDetails = new ArcInfo.LayerDetails[count];
// fill the individual arc layer structures
int layerIndex = 0;
for(int k=0; k<2; k++)
{
for(Sample ns : arcEx.samples)
{
if (ns.layer == null) continue;
// get the layer index
String sampleLayer = ns.layer.getName().substring(6);
LayerInfo li = null;
for(int j=0; j<lList.length; j++)
{
if (sampleLayer.equals(lList[j].name)) { li = lList[j]; break; }
}
if (li == null)
{
error.markError(ns.node, np, "Unknown layer: " + sampleLayer);
return null;
}
// only add transparent layers when k=0
if (k == 0)
{
if (li.desc.getTransparentLayer() == 0) continue;
} else
{
if (li.desc.getTransparentLayer() != 0) continue;
}
allArcs[i].arcDetails[layerIndex] = new ArcInfo.LayerDetails();
allArcs[i].arcDetails[layerIndex].layer = li;
// determine the style of this arc layer
Poly.Type style = Poly.Type.CLOSED;
if (ns.node.getProto() == Artwork.tech().filledBoxNode)
style = Poly.Type.FILLED;
allArcs[i].arcDetails[layerIndex].style = style;
// determine the width offset of this arc layer
double wid = Math.min(ns.node.getXSize(), ns.node.getYSize());
allArcs[i].arcDetails[layerIndex].width = wid;
layerIndex++;
}
}
}
return allArcs;
}
/************************************* NODE ANALYSIS *************************************/
/**
* Method to scan the "dependentlibcount" libraries in "dependentLibs",
* and build the node structures for it in technology "tech".
*/
private NodeInfo [] extractNodes(Library [] dependentLibs, LayerInfo [] lList, ArcInfo [] aList)
{
Cell [] nodeCells = Info.findCellSequence(dependentLibs, "node-", Info.NODESEQUENCE_KEY);
if (nodeCells.length <= 0)
{
System.out.println("No nodes found");
return null;
}
List<NodeInfo> nList = new ArrayList<NodeInfo>();
// get the nodes
for(int pass=0; pass<3; pass++)
for(Cell np : nodeCells)
{
// make sure this is the right type of node for this pass of the nodes
NodeInfo nIn = NodeInfo.parseCell(np);
// only want pins on pass 0, pure-layer nodes on pass 2
if (pass == 0 && !nIn.func.isPin()) continue;
if (pass == 1 && (nIn.func.isPin() || nIn.func == PrimitiveNode.Function.NODE)) continue;
if (pass == 2 && nIn.func != PrimitiveNode.Function.NODE) continue;
if (nIn.func == PrimitiveNode.Function.NODE)
{
if (nIn.serp)
{
error.markError(null, np, "Pure layer " + nIn.name + " can not be serpentine");
return null;
}
nIn.specialType = PrimitiveNode.POLYGONAL;
}
// build a list of examples found in this node
List<Example> variations = new ArrayList<Example>();
List<Example> neList = Example.getExamples(np, true, error, variations);
if (neList == null || neList.size() == 0)
{
System.out.println("Cannot analyze " + np);
return null;
}
// sort the layers in the main example
Example firstEx = neList.get(0);
Collections.sort(firstEx.samples, new SamplesByLayerOrder(lList));
nIn.xSize = firstEx.hx - firstEx.lx;
nIn.ySize = firstEx.hy - firstEx.ly;
nIn.name = np.getName().substring(5);
nList.add(nIn);
// add in variation names
if (variations.size() > 0)
{
nIn.primitiveNodeGroupNames = new ArrayList<String>();
nIn.primitiveNodeGroupNames.add(nIn.name);
for(Example variation : variations)
{
for(Sample s : variation.samples)
{
if (!s.node.getNameKey().isTempname())
{
nIn.primitiveNodeGroupNames.add(s.node.getName());
break;
}
}
}
}
// associate the samples in each example
if (associateExamples(neList, np)) return null;
// derive primitives from the examples
nIn.nodeLayers = makePrimitiveNodeLayers(neList, np, lList, variations);
if (nIn.nodeLayers == null) return null;
if (fillPrimitivePorts(np, nIn, firstEx, aList)) return null;
if (fillSizeOffset(np, firstEx, nIn)) return null;
}
NodeInfo [] nListArray = new NodeInfo[nList.size()];
for(int i=0; i<nList.size(); i++)
nListArray[i] = nList.get(i);
return nListArray;
}
/**
* Method to load the size offset information for a new PrimitiveNode.
* @param np the Cell that describes the PrimitiveNode.
* @param firstEx the Example that describes the PrimitiveNode.
* @param nIn the new PrimitiveNode information being created.
* @return true on error.
*/
private boolean fillSizeOffset(Cell np, Example firstEx, NodeInfo nIn)
{
// extract width offset information from highlight box
double lX = 0, hX = 0, lY = 0, hY = 0;
boolean found = false;
for(Sample ns : firstEx.samples)
{
if (ns.layer != null) continue;
found = true;
if (ns.values != null)
{
boolean err = false;
if (ns.values[0].getX().getMultiplier() == -0.5) // left edge offset
{
lX = ns.values[0].getX().getAdder();
} else if (ns.values[0].getX().getMultiplier() == 0.5)
{
lX = nIn.xSize + ns.values[0].getX().getAdder();
} else err = true;
if (ns.values[0].getY().getMultiplier() == -0.5) // bottom edge offset
{
lY = ns.values[0].getY().getAdder();
} else if (ns.values[0].getY().getMultiplier() == 0.5)
{
lY = nIn.ySize + ns.values[0].getY().getAdder();;
} else err = true;
if (ns.values[1].getX().getMultiplier() == 0.5) // right edge offset
{
hX = -ns.values[1].getX().getAdder();
} else if (ns.values[1].getX().getMultiplier() == -0.5)
{
hX = nIn.xSize - ns.values[1].getX().getAdder();
} else err = true;
if (ns.values[1].getY().getMultiplier() == 0.5) // top edge offset
{
hY = -ns.values[1].getY().getAdder();
} else if (ns.values[1].getY().getMultiplier() == -0.5)
{
hY = nIn.ySize - ns.values[1].getY().getAdder();
} else err = true;
if (err)
{
error.markError(ns.node, np, "Highlighting cannot scale from center");
return true;
}
} else
{
error.markError(ns.node, np, "No rule found for highlight");
return true;
}
}
if (!found)
{
error.markError(null, np, "No highlight found");
return true;
}
if (lX != 0 || hX != 0 || lY != 0 || hY != 0)
{
nIn.so = new SizeOffset(lX, hX, lY, hY);
}
// // get grab point information
// for(ns = neList.firstSample; ns != NOSAMPLE; ns = ns.nextSample)
// if (ns.layer == Generic.tech.cellCenterNode) break;
// if (ns != NOSAMPLE)
// {
// us_tecnode_grab[us_tecnode_grabcount++] = nodeindex+1;
// us_tecnode_grab[us_tecnode_grabcount++] = (ns.node.geom.lowx +
// ns.node.geom.highx - neList.lx - neList.hx)/2 *
// el_curlib.lambda[tech.techindex] / lambda;
// us_tecnode_grab[us_tecnode_grabcount++] = (ns.node.geom.lowy +
// ns.node.geom.highy - neList.ly - neList.hy)/2 *
// el_curlib.lambda[tech.techindex] / lambda;
// us_tecflags |= HASGRAB;
// }
return false;
}
/**
* Method to fill the ports in a newly-built primitiveNode.
* @param np the Cell that describes the PrimitiveNode.
* @param nIn the new PrimitiveNode information being created.
* @param firstEx the Example that describes the PrimitiveNode.
* @param aList the list of Arcs already in the new Technology.
* @return true on error.
*/
private boolean fillPrimitivePorts(Cell np, NodeInfo nIn, Example firstEx, ArcInfo [] aList)
{
Netlist netList = np.getNetlist();
if (netList == null)
{
System.out.println("Sorry, a deadlock technology generation (network information unavailable). Please try again");
return true;
}
// fill the port structures
List<NodeInfo.PortDetails> ports = new ArrayList<NodeInfo.PortDetails>();
Map<NodeInfo.PortDetails,Sample> portSamples = new HashMap<NodeInfo.PortDetails,Sample>();
for(Sample ns : firstEx.samples)
{
if (ns.layer != Generic.tech().portNode) continue;
// port connections
NodeInfo.PortDetails nipd = new NodeInfo.PortDetails();
portSamples.put(nipd, ns);
// port name
nipd.name = Info.getPortName(ns.node);
if (nipd.name == null)
{
error.markError(ns.node, np, "Port does not have a name");
return true;
}
for(int c=0; c<nipd.name.length(); c++)
{
char str = nipd.name.charAt(c);
if (str <= ' ' || str >= 0177)
{
error.markError(ns.node, np, "Invalid port name");
return true;
}
}
// port angle and range
nipd.angle = 0;
Variable varAngle = ns.node.getVar(Info.PORTANGLE_KEY);
if (varAngle != null)
nipd.angle = ((Integer)varAngle.getObject()).intValue();
nipd.range = 180;
Variable varRange = ns.node.getVar(Info.PORTRANGE_KEY);
if (varRange != null)
nipd.range = ((Integer)varRange.getObject()).intValue();
// port area rule
nipd.values = ns.values;
ports.add(nipd);
}
// sort the ports by name within angle
Collections.sort(ports, new PortsByAngleAndName());
// now find the poly/active ports for transistor rearranging
int pol1Port = -1, pol2Port = -1, dif1Port = -1, dif2Port = -1;
int m1Port1 = -1, m1Port2 = -1, m2Port1 = -1, m2Port2 = -1;
for(int i=0; i<ports.size(); i++)
{
NodeInfo.PortDetails nipd = ports.get(i);
Sample ns = portSamples.get(nipd);
nipd.connections = new ArcInfo[0];
Variable var = ns.node.getVar(Info.CONNECTION_KEY);
if (var != null)
{
// convert "arc-CELL" pointers to indices
CellId [] arcCells = (CellId [])var.getObject();
List<ArcInfo> validArcCells = new ArrayList<ArcInfo>();
for(int j=0; j<arcCells.length; j++)
{
// find arc that connects
if (arcCells[j] == null) continue;
Cell arcCell = EDatabase.serverDatabase().getCell(arcCells[j]);
if (arcCell == null) continue;
String cellName = arcCell.getName().substring(4);
for(int k=0; k<aList.length; k++)
{
if (aList[k].name.equalsIgnoreCase(cellName))
{
validArcCells.add(aList[k]);
break;
}
}
}
ArcInfo [] connections = new ArcInfo[validArcCells.size()];
nipd.connections = connections;
for(int j=0; j<validArcCells.size(); j++)
connections[j] = validArcCells.get(j);
for(int j=0; j<connections.length; j++)
{
// find port characteristics for possible transistors
Variable meaningVar = ns.node.getVar(Info.PORTMEANING_KEY);
int meaning = 0;
if (meaningVar != null) meaning = ((Integer)meaningVar.getObject()).intValue();
Function fun = connections[j].func;
if (fun.isPoly() || meaning == 1)
{
if (pol1Port < 0) pol1Port = i; else
if (pol2Port < 0) pol2Port = i;
} else if (fun.isDiffusion() || meaning == 2)
{
if (dif1Port < 0) dif1Port = i; else
if (dif2Port < 0) dif2Port = i;
}
if ((fun.isMetal() && fun.getLevel() == 1) || meaning == 1)
{
if (m1Port1 < 0) m1Port1 = i; else
if (m1Port2 < 0) m1Port2 = i;
} else if ((fun.isMetal() && fun.getLevel() == 2) || meaning == 2)
{
if (m2Port1 < 0) m2Port1 = i; else
if (m2Port2 < 0) m2Port2 = i;
}
}
}
}
// save the ports in an array
nIn.nodePortDetails = new NodeInfo.PortDetails[ports.size()];
for(int j=0; j<ports.size(); j++) nIn.nodePortDetails[j] = ports.get(j);
// establish port connectivity
for(int i=0; i<nIn.nodePortDetails.length; i++)
{
NodeInfo.PortDetails nipd = nIn.nodePortDetails[i];
Sample ns = portSamples.get(nipd);
nipd.netIndex = i;
if (ns.node.hasConnections())
{
ArcInst ai1 = ns.node.getConnections().next().getArc();
Network net1 = netList.getNetwork(ai1, 0);
for(int j=0; j<i; j++)
{
NodeInfo.PortDetails onipd = nIn.nodePortDetails[j];
Sample oNs = portSamples.get(onipd);
if (oNs.node.hasConnections())
{
ArcInst ai2 = oNs.node.getConnections().next().getArc();
Network net2 = netList.getNetwork(ai2, 0);
if (net1 == net2)
{
nipd.netIndex = j;
break;
}
}
}
}
}
// on MOS transistors, make sure the first 4 ports are poly/active/poly/active
if (nIn.func.isFET())
{
if (pol1Port < 0 || pol2Port < 0 || dif1Port < 0 || dif2Port < 0)
{
// in TFT transistors, its metal1 crossing metal2
if (m1Port1 >= 0 && m1Port2 >= 0 && m2Port1 >= 0 && m2Port2 >= 0)
{
pol1Port = m1Port1;
pol2Port = m1Port2;
dif1Port = m2Port1;
dif2Port = m2Port2;
} else
{
error.markError(null, np, "Need 2 gate (poly/m1) and 2 gated (active/m2) ports on field-effect transistor");
return true;
}
}
// also make sure that dif1Port is positive and dif2Port is negative
double x1Pos = (nIn.nodePortDetails[dif1Port].values[0].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[dif1Port].values[0].getX().getAdder() +
nIn.nodePortDetails[dif1Port].values[1].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[dif1Port].values[1].getX().getAdder()) / 2;
double x2Pos = (nIn.nodePortDetails[dif2Port].values[0].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[dif2Port].values[0].getX().getAdder() +
nIn.nodePortDetails[dif2Port].values[1].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[dif2Port].values[1].getX().getAdder()) / 2;
double y1Pos = (nIn.nodePortDetails[dif1Port].values[0].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[dif1Port].values[0].getY().getAdder() +
nIn.nodePortDetails[dif1Port].values[1].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[dif1Port].values[1].getY().getAdder()) / 2;
double y2Pos = (nIn.nodePortDetails[dif2Port].values[0].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[dif2Port].values[0].getY().getAdder() +
nIn.nodePortDetails[dif2Port].values[1].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[dif2Port].values[1].getY().getAdder()) / 2;
if (Math.abs(x1Pos-x2Pos) > Math.abs(y1Pos-y2Pos))
{
if (x1Pos < x2Pos)
{
int k = dif1Port; dif1Port = dif2Port; dif2Port = k;
}
} else
{
if (y1Pos < y2Pos)
{
int k = dif1Port; dif1Port = dif2Port; dif2Port = k;
}
}
// also make sure that pol1Port is negative and pol2Port is positive
x1Pos = (nIn.nodePortDetails[pol1Port].values[0].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[pol1Port].values[0].getX().getAdder() +
nIn.nodePortDetails[pol1Port].values[1].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[pol1Port].values[1].getX().getAdder()) / 2;
x2Pos = (nIn.nodePortDetails[pol2Port].values[0].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[pol2Port].values[0].getX().getAdder() +
nIn.nodePortDetails[pol2Port].values[1].getX().getMultiplier() * nIn.xSize +
nIn.nodePortDetails[pol2Port].values[1].getX().getAdder()) / 2;
y1Pos = (nIn.nodePortDetails[pol1Port].values[0].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[pol1Port].values[0].getY().getAdder() +
nIn.nodePortDetails[pol1Port].values[1].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[pol1Port].values[1].getY().getAdder()) / 2;
y2Pos = (nIn.nodePortDetails[pol2Port].values[0].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[pol2Port].values[0].getY().getAdder() +
nIn.nodePortDetails[pol2Port].values[1].getY().getMultiplier() * nIn.ySize +
nIn.nodePortDetails[pol2Port].values[1].getY().getAdder()) / 2;
if (Math.abs(x1Pos-x2Pos) > Math.abs(y1Pos-y2Pos))
{
if (x1Pos > x2Pos)
{
int k = pol1Port; pol1Port = pol2Port; pol2Port = k;
}
} else
{
if (y1Pos > y2Pos)
{
int k = pol1Port; pol1Port = pol2Port; pol2Port = k;
}
}
// gather extra ports that go at the end
List<NodeInfo.PortDetails> extras = new ArrayList<NodeInfo.PortDetails>();
for(int j=0; j<ports.size(); j++)
{
if (j != pol1Port && j != dif1Port && j != pol2Port && j != dif2Port)
extras.add(ports.get(j));
}
// rearrange the ports
NodeInfo.PortDetails port0 = nIn.nodePortDetails[pol1Port];
NodeInfo.PortDetails port1 = nIn.nodePortDetails[dif1Port];
NodeInfo.PortDetails port2 = nIn.nodePortDetails[pol2Port];
NodeInfo.PortDetails port3 = nIn.nodePortDetails[dif2Port];
nIn.nodePortDetails[pol1Port=0] = port0;
nIn.nodePortDetails[dif1Port=1] = port1;
nIn.nodePortDetails[pol2Port=2] = port2;
nIn.nodePortDetails[dif2Port=3] = port3;
for(int j=0; j<extras.size(); j++)
nIn.nodePortDetails[j+4] = extras.get(j);
// make sure implant layers are not connected to ports
for(int k=0; k<nIn.nodeLayers.length; k++)
{
NodeInfo.LayerDetails nld = nIn.nodeLayers[k];
if (nld.layer.fun.isSubstrate()) nld.portIndex = -1;
}
}
if (nIn.serp)
{
// finish up serpentine transistors
nIn.specialType = PrimitiveNode.SERPTRANS;
// determine port numbers for serpentine transistors
int polIndex = -1, difIndex = -1;
for(int k=0; k<nIn.nodeLayers.length; k++)
{
NodeInfo.LayerDetails nld = nIn.nodeLayers[k];
if (nld.layer.fun.isPoly())
{
polIndex = k;
} else if (nld.layer.fun.isDiff())
{
if (difIndex >= 0)
{
// figure out which layer is the basic active layer
int funExtraOld = nIn.nodeLayers[difIndex].layer.funExtra;
int funExtraNew = nld.layer.funExtra;
if (funExtraOld == funExtraNew) continue;
if (funExtraOld == 0) continue;
}
difIndex = k;
}
}
if (difIndex < 0 || polIndex < 0)
{
error.markError(null, np, "No diffusion and polysilicon layers in serpentine transistor");
return true;
}
// find width and extension from comparison to poly layer
Sample polNs = nIn.nodeLayers[polIndex].ns;
Rectangle2D polNodeBounds = polNs.node.getBounds();
Sample difNs = nIn.nodeLayers[difIndex].ns;
Rectangle2D difNodeBounds = difNs.node.getBounds();
for(int k=0; k<nIn.nodeLayers.length; k++)
{
NodeInfo.LayerDetails nld = nIn.nodeLayers[k];
Sample ns = nld.ns;
Rectangle2D nodeBounds = ns.node.getBounds();
if (polNodeBounds.getWidth() > polNodeBounds.getHeight())
{
// horizontal layer
nld.lWidth = nodeBounds.getMaxY() - (ns.parent.ly + ns.parent.hy)/2;
nld.rWidth = (ns.parent.ly + ns.parent.hy)/2 - nodeBounds.getMinY();
nld.extendT = difNodeBounds.getMinX() - nodeBounds.getMinX();
} else
{
// vertical layer
nld.lWidth = nodeBounds.getMaxX() - (ns.parent.lx + ns.parent.hx)/2;
nld.rWidth = (ns.parent.lx + ns.parent.hx)/2 - nodeBounds.getMinX();
nld.extendT = difNodeBounds.getMinY() - nodeBounds.getMinY();
}
nld.extendB = nld.extendT;
}
// add in electrical layers for diffusion
NodeInfo.LayerDetails [] addedLayers = new NodeInfo.LayerDetails[nIn.nodeLayers.length+2];
for(int k=0; k<nIn.nodeLayers.length; k++)
addedLayers[k] = nIn.nodeLayers[k];
NodeInfo.LayerDetails diff1 = nIn.nodeLayers[difIndex].duplicate();
NodeInfo.LayerDetails diff2 = nIn.nodeLayers[difIndex].duplicate();
addedLayers[nIn.nodeLayers.length] = diff1;
addedLayers[nIn.nodeLayers.length+1] = diff2;
nIn.nodeLayers = addedLayers;
diff1.inLayers = diff2.inLayers = false;
nIn.nodeLayers[difIndex].inElectricalLayers = false;
diff1.portIndex = dif1Port;
diff2.portIndex = dif2Port;
// compute port extension factors
nIn.specialValues = new double[6];
int layerCount = 0;
for(Sample ns : firstEx.samples)
{
if (ns.values != null && ns.layer != Generic.tech().portNode &&
ns.layer != Generic.tech().cellCenterNode && ns.layer != null)
layerCount++;
}
nIn.specialValues[0] = layerCount+1;
if (nIn.nodePortDetails[dif1Port].values[0].getX().getAdder() >
nIn.nodePortDetails[dif1Port].values[0].getY().getAdder())
{
// vertical diffusion layer: determine polysilicon width
nIn.specialValues[3] = (nIn.ySize * nIn.nodeLayers[polIndex].values[1].getY().getMultiplier() +
nIn.nodeLayers[polIndex].values[1].getY().getAdder()) -
(nIn.ySize * nIn.nodeLayers[polIndex].values[0].getY().getMultiplier() +
nIn.nodeLayers[polIndex].values[0].getY().getAdder());
// determine diffusion port rule
nIn.specialValues[1] = (nIn.xSize * nIn.nodePortDetails[dif1Port].values[0].getX().getMultiplier() +
nIn.nodePortDetails[dif1Port].values[0].getX().getAdder()) -
(nIn.xSize * nIn.nodeLayers[difIndex].values[0].getX().getMultiplier() +
nIn.nodeLayers[difIndex].values[0].getX().getAdder());
nIn.specialValues[2] = (nIn.ySize * nIn.nodePortDetails[dif1Port].values[0].getY().getMultiplier() +
nIn.nodePortDetails[dif1Port].values[0].getY().getAdder()) -
(nIn.ySize * nIn.nodeLayers[polIndex].values[1].getY().getMultiplier() +
nIn.nodeLayers[polIndex].values[1].getY().getAdder());
// determine polysilicon port rule
nIn.specialValues[4] = (nIn.ySize * nIn.nodePortDetails[pol1Port].values[0].getY().getMultiplier() +
nIn.nodePortDetails[pol1Port].values[0].getY().getAdder()) -
(nIn.ySize * nIn.nodeLayers[polIndex].values[0].getY().getMultiplier() +
nIn.nodeLayers[polIndex].values[0].getY().getAdder());
nIn.specialValues[5] = (nIn.xSize * nIn.nodeLayers[difIndex].values[0].getX().getMultiplier() +
nIn.nodeLayers[difIndex].values[0].getX().getAdder()) -
(nIn.xSize * nIn.nodePortDetails[pol1Port].values[1].getX().getMultiplier() +
nIn.nodePortDetails[pol1Port].values[1].getX().getAdder());
// setup electrical layers for diffusion
diff1.values[0] = diff1.values[0].withX(new EdgeH(0,0));
// diff1.values[0].getY().setMultiplier(0);
// diff1.values[0].getY().setAdder(0);
diff1.rWidth = 0;
diff2.values[1] = diff2.values[0].withY(new EdgeV(0,0));
// diff2.values[1].getY().setMultiplier(0);
// diff2.values[1].getY().setAdder(0);
diff2.lWidth = 0;
} else
{
// horizontal diffusion layer: determine polysilicon width
nIn.specialValues[3] = (nIn.xSize * nIn.nodeLayers[polIndex].values[1].getX().getMultiplier() +
nIn.nodeLayers[polIndex].values[1].getX().getAdder()) -
(nIn.xSize * nIn.nodeLayers[polIndex].values[0].getX().getMultiplier() +
nIn.nodeLayers[polIndex].values[0].getX().getAdder());
// determine diffusion port rule
nIn.specialValues[1] = (nIn.ySize * nIn.nodePortDetails[dif1Port].values[0].getY().getMultiplier() +
nIn.nodePortDetails[dif1Port].values[0].getY().getAdder()) -
(nIn.ySize * nIn.nodeLayers[difIndex].values[0].getY().getMultiplier() +
nIn.nodeLayers[difIndex].values[0].getY().getAdder());
nIn.specialValues[2] = (nIn.xSize * nIn.nodeLayers[polIndex].values[0].getX().getMultiplier() +
nIn.nodeLayers[polIndex].values[0].getX().getAdder()) -
(nIn.xSize * nIn.nodePortDetails[dif1Port].values[1].getX().getMultiplier() +
nIn.nodePortDetails[dif1Port].values[1].getX().getAdder());
// determine polysilicon port rule
nIn.specialValues[4] = (nIn.xSize * nIn.nodePortDetails[pol1Port].values[0].getX().getMultiplier() +
nIn.nodePortDetails[pol1Port].values[0].getX().getAdder()) -
(nIn.xSize * nIn.nodeLayers[polIndex].values[0].getX().getMultiplier() +
nIn.nodeLayers[polIndex].values[0].getX().getAdder());
nIn.specialValues[5] = (nIn.ySize * nIn.nodeLayers[difIndex].values[0].getY().getMultiplier() +
nIn.nodeLayers[difIndex].values[0].getY().getAdder()) -
(nIn.ySize * nIn.nodePortDetails[pol1Port].values[1].getY().getMultiplier() +
nIn.nodePortDetails[pol1Port].values[1].getY().getAdder());
// setup electrical layers for diffusion
diff1.values[0] = diff1.values[0].withX(new EdgeH(0, 0));
// diff1.values[0].getX().setMultiplier(0);
// diff1.values[0].getX().setAdder(0);
diff1.rWidth = 0;
diff2.values[1] = diff2.values[1].withX(new EdgeH(0, 0));
// diff2.values[1].getX().setMultiplier(0);
// diff2.values[1].getX().setAdder(0);
diff2.lWidth = 0;
}
}
return false;
}
private NodeInfo.LayerDetails [] makePrimitiveNodeLayers(List<Example> neList, Cell np, LayerInfo [] lis,
List<Example> variations)
{
// if there is only one example: make sample scale with edge
Example firstEx = neList.get(0);
if (neList.size() <= 1)
{
return makeNodeScaledUniformly(neList, np, lis, null, null);
}
List<NodeInfo.LayerDetails> nodeLayersList = new ArrayList<NodeInfo.LayerDetails>();
// look at every sample "ns" in the main example "neList"
for(Sample ns : firstEx.samples)
{
// ignore grab point specification
if (ns.layer == Generic.tech().cellCenterNode) continue;
AffineTransform trans = ns.node.rotateOut();
Rectangle2D nodeBounds = getBoundingBox(ns.node);
// determine the layer
LayerInfo giLayer = null;
if (ns.layer != null && ns.layer != Generic.tech().portNode)
{
String desiredLayer = ns.layer.getName().substring(6);
for(int i=0; i<lis.length; i++)
{
if (desiredLayer.equals(lis[i].name)) { giLayer = lis[i]; break; }
}
if (giLayer == null)
{
System.out.println("Cannot find layer " + desiredLayer);
return null;
}
}
// look at other examples and find samples associated with this
firstEx.studySample = ns;
NodeInfo.LayerDetails multiRule = null;
for(int n=1; n<neList.size(); n++)
{
Example ne = neList.get(n);
// count number of samples associated with the main sample
int total = 0;
for(Sample nso : ne.samples)
{
if (nso.assoc == ns)
{
ne.studySample = nso;
total++;
}
}
if (total == 0)
{
error.markError(ns.node, np, "Still unassociated sample (shouldn't happen)");
return null;
}
// if there are multiple associations, it must be a contact cut
if (total > 1)
{
// make sure the layer is real geometry, not highlight or a port
if (ns.layer == null || ns.layer == Generic.tech().portNode)
{
error.markError(ns.node, np, "Only contact layers may be iterated in examples");
return null;
}
// add the rule
multiRule = getMultiCutRule(ns, neList, np);
if (multiRule != null) break;
}
}
if (multiRule != null)
{
multiRule.layer = giLayer;
nodeLayersList.add(multiRule);
continue;
}
// associations done for this sample, now analyze them
Point2D [] pointList = null;
int [] pointFactor = null;
Point2D [] points = null;
if (ns.node.getProto() == Artwork.tech().filledPolygonNode ||
ns.node.getProto() == Artwork.tech().closedPolygonNode ||
ns.node.getProto() == Artwork.tech().openedPolygonNode ||
ns.node.getProto() == Artwork.tech().openedDottedPolygonNode ||
ns.node.getProto() == Artwork.tech().openedDashedPolygonNode ||
ns.node.getProto() == Artwork.tech().openedThickerPolygonNode)
{
points = ns.node.getTrace();
}
int trueCount = 0;
int minFactor = 0;
if (points != null)
{
// make sure the arrays hold "count" points
pointList = new Point2D[points.length];
pointFactor = new int[points.length];
for(int i=0; i<points.length; i++)
{
pointList[i] = new Point2D.Double(nodeBounds.getCenterX() + points[i].getX(),
nodeBounds.getCenterY() + points[i].getY());
trans.transform(pointList[i], pointList[i]);
}
trueCount = points.length;
} else
{
double [] angles = null;
if (ns.node.getProto() == Artwork.tech().circleNode || ns.node.getProto() == Artwork.tech().thickCircleNode)
{
angles = ns.node.getArcDegrees();
if (angles[0] == 0 && angles[1] == 0) angles = null;
}
// if (angles == null)
// {
// Variable var2 = ns.node.getVar(Info.MINSIZEBOX_KEY);
// if (var2 != null) minFactor = 2;
// }
// set sample description
if (angles != null)
{
// handle circular arc sample
pointList = new Point2D[3];
pointFactor = new int[3];
pointList[0] = new Point2D.Double(nodeBounds.getCenterX(), nodeBounds.getCenterY());
double dist = nodeBounds.getMaxX() - nodeBounds.getCenterX();
pointList[1] = new Point2D.Double(nodeBounds.getCenterX() + dist * Math.cos(angles[0]),
nodeBounds.getCenterY() + dist * Math.sin(angles[0]));
trans.transform(pointList[1], pointList[1]);
trueCount = 3;
} else if (ns.node.getProto() == Artwork.tech().circleNode || ns.node.getProto() == Artwork.tech().thickCircleNode ||
ns.node.getProto() == Artwork.tech().filledCircleNode)
{
// handle circular sample
pointList = new Point2D[2+minFactor];
pointFactor = new int[2+minFactor];
pointList[0] = new Point2D.Double(nodeBounds.getCenterX(), nodeBounds.getCenterY());
pointList[1] = new Point2D.Double(nodeBounds.getMaxX(), nodeBounds.getCenterY());
trueCount = 2;
} else
{
// rectangular sample: get the bounding box in (pointListx, pointListy)
pointList = new Point2D[2+minFactor];
pointFactor = new int[2+minFactor];
pointList[0] = new Point2D.Double(nodeBounds.getMinX(), nodeBounds.getMinY());
pointList[1] = new Point2D.Double(nodeBounds.getMaxX(), nodeBounds.getMaxY());
trueCount = 2;
}
// if (minFactor > 1)
// {
// pointList[2] = new Point2D.Double(pointList[0].getX(),pointList[0].getY());
// pointList[3] = new Point2D.Double(pointList[1].getX(),pointList[1].getY());
// }
}
double [] pointLeftDist = new double[pointFactor.length];
double [] pointRightDist = new double[pointFactor.length];
double [] pointBottomDist = new double[pointFactor.length];
double [] pointTopDist = new double[pointFactor.length];
double [] centerXDist = new double[pointFactor.length];
double [] centerYDist = new double[pointFactor.length];
double [] pointXRatio = new double[pointFactor.length];
double [] pointYRatio = new double[pointFactor.length];
for(int i=0; i<pointFactor.length; i++)
{
pointLeftDist[i] = pointList[i].getX() - firstEx.lx;
pointRightDist[i] = firstEx.hx - pointList[i].getX();
pointBottomDist[i] = pointList[i].getY() - firstEx.ly;
pointTopDist[i] = firstEx.hy - pointList[i].getY();
centerXDist[i] = pointList[i].getX() - (firstEx.lx+firstEx.hx)/2;
centerYDist[i] = pointList[i].getY() - (firstEx.ly+firstEx.hy)/2;
if (firstEx.hx == firstEx.lx) pointXRatio[i] = 0; else
pointXRatio[i] = (pointList[i].getX() - (firstEx.lx+firstEx.hx)/2) / (firstEx.hx-firstEx.lx);
if (firstEx.hy == firstEx.ly) pointYRatio[i] = 0; else
pointYRatio[i] = (pointList[i].getY() - (firstEx.ly+firstEx.hy)/2) / (firstEx.hy-firstEx.ly);
if (i < trueCount)
pointFactor[i] = TOEDGELEFT | TOEDGERIGHT | TOEDGETOP | TOEDGEBOT | FROMCENTX |
FROMCENTY | RATIOCENTX | RATIOCENTY; else
pointFactor[i] = FROMCENTX | FROMCENTY;
}
Point2D [] pointCoords = new Point2D[pointFactor.length];
for(int n = 1; n<neList.size(); n++)
{
Example ne = neList.get(n);
NodeInst ni = ne.studySample.node;
AffineTransform oTrans = ni.rotateOut();
Rectangle2D oNodeBounds = getBoundingBox(ni);
Point2D [] oPoints = null;
if (ni.getProto() == Artwork.tech().filledPolygonNode ||
ni.getProto() == Artwork.tech().closedPolygonNode ||
ni.getProto() == Artwork.tech().openedPolygonNode ||
ni.getProto() == Artwork.tech().openedDottedPolygonNode ||
ni.getProto() == Artwork.tech().openedDashedPolygonNode ||
ni.getProto() == Artwork.tech().openedThickerPolygonNode)
{
oPoints = ni.getTrace();
}
int newCount = 2;
if (oPoints != null)
{
newCount = oPoints.length;
int numPoints = Math.min(trueCount, newCount);
int bestOffset = 0;
double bestDist = Double.MAX_VALUE;
for(int offset = 0; offset < numPoints; offset++)
{
// determine total distance between points
double dist = 0;
for(int i=0; i<numPoints; i++)
{
double dX = points[i].getX() - oPoints[(i+offset)%numPoints].getX();
double dY = points[i].getY() - oPoints[(i+offset)%numPoints].getY();
dist += Math.hypot(dX, dY);
}
if (dist < bestDist)
{
bestDist = dist;
bestOffset = offset;
}
}
for(int i=0; i<numPoints; i++)
{
pointCoords[i] = new Point2D.Double(oNodeBounds.getCenterX() + oPoints[(i+bestOffset)%numPoints].getX(),
oNodeBounds.getCenterY() + oPoints[(i+bestOffset)%numPoints].getY());
oTrans.transform(pointCoords[i], pointCoords[i]);
}
} else
{
double [] angles = null;
if (ni.getProto() == Artwork.tech().circleNode || ni.getProto() == Artwork.tech().thickCircleNode)
{
angles = ni.getArcDegrees();
if (angles[0] == 0 && angles[1] == 0) angles = null;
}
if (angles != null)
{
pointCoords[0] = new Point2D.Double(oNodeBounds.getCenterX(), oNodeBounds.getCenterY());
double dist = oNodeBounds.getMaxX() - oNodeBounds.getCenterX();
pointCoords[1] = new Point2D.Double(oNodeBounds.getCenterX() + dist * Math.cos(angles[0]),
oNodeBounds.getCenterY() + dist * Math.sin(angles[0]));
oTrans.transform(pointCoords[1], pointCoords[1]);
} else if (ni.getProto() == Artwork.tech().circleNode || ni.getProto() == Artwork.tech().thickCircleNode ||
ni.getProto() == Artwork.tech().filledCircleNode)
{
pointCoords[0] = new Point2D.Double(oNodeBounds.getCenterX(), oNodeBounds.getCenterY());
pointCoords[1] = new Point2D.Double(oNodeBounds.getMaxX(), oNodeBounds.getCenterY());
} else
{
pointCoords[0] = new Point2D.Double(oNodeBounds.getMinX(), oNodeBounds.getMinY());
pointCoords[1] = new Point2D.Double(oNodeBounds.getMaxX(), oNodeBounds.getMaxY());
}
}
if (newCount != trueCount)
{
error.markError(ni, np, "Main example of layer " + Info.getSampleName(ne.studySample.layer) +
" has " + trueCount + " points but this has " + newCount);
return null;
}
for(int i=0; i<trueCount; i++)
{
// see if edges are fixed distance from example edge
if (!DBMath.areEquals(pointLeftDist[i], pointCoords[i].getX() - ne.lx)) pointFactor[i] &= ~TOEDGELEFT;
if (!DBMath.areEquals(pointRightDist[i], ne.hx - pointCoords[i].getX())) pointFactor[i] &= ~TOEDGERIGHT;
if (!DBMath.areEquals(pointBottomDist[i], pointCoords[i].getY() - ne.ly)) pointFactor[i] &= ~TOEDGEBOT;
if (!DBMath.areEquals(pointTopDist[i], ne.hy - pointCoords[i].getY())) pointFactor[i] &= ~TOEDGETOP;
// see if edges are fixed distance from example center
if (!DBMath.areEquals(centerXDist[i], pointCoords[i].getX() - (ne.lx+ne.hx)/2)) pointFactor[i] &= ~FROMCENTX;
if (!DBMath.areEquals(centerYDist[i], pointCoords[i].getY() - (ne.ly+ne.hy)/2)) pointFactor[i] &= ~FROMCENTY;
// see if edges are fixed ratio from example center
double r = 0;
if (ne.hx != ne.lx)
r = (pointCoords[i].getX() - (ne.lx+ne.hx)/2) / (ne.hx-ne.lx);
if (!DBMath.areEquals(r, pointXRatio[i])) pointFactor[i] &= ~RATIOCENTX;
if (ne.hy == ne.ly) r = 0; else
r = (pointCoords[i].getY() - (ne.ly+ne.hy)/2) / (ne.hy-ne.ly);
if (!DBMath.areEquals(r, pointYRatio[i])) pointFactor[i] &= ~RATIOCENTY;
}
// make sure port information is on the primary example
if (ns.layer != Generic.tech().portNode) continue;
// check port angle
Variable var = ns.node.getVar(Info.PORTANGLE_KEY);
Variable var2 = ni.getVar(Info.PORTANGLE_KEY);
if (var == null && var2 != null)
{
System.out.println("Warning: moving port angle to main example of " + np);
ns.node.newVar(Info.PORTANGLE_KEY, var2.getObject());
}
// check port range
var = ns.node.getVar(Info.PORTRANGE_KEY);
var2 = ni.getVar(Info.PORTRANGE_KEY);
if (var == null && var2 != null)
{
System.out.println("Warning: moving port range to main example of " + np);
ns.node.newVar(Info.PORTRANGE_KEY, var2.getObject());
}
// check connectivity
var = ns.node.getVar(Info.CONNECTION_KEY);
var2 = ni.getVar(Info.CONNECTION_KEY);
if (var == null && var2 != null)
{
System.out.println("Warning: moving port connections to main example of " + np);
ns.node.newVar(Info.CONNECTION_KEY, var2.getObject());
}
}
// error check for the highlight layer
if (ns.layer == null)
{
for(int i=0; i<trueCount; i++)
if ((pointFactor[i]&(TOEDGELEFT|TOEDGERIGHT)) == 0 ||
(pointFactor[i]&(TOEDGETOP|TOEDGEBOT)) == 0)
{
error.markError(ns.node, np, "Highlight must be constant distance from edge");
return null;
}
}
// finally, make a rule for this sample
Technology.TechPoint [] newRule = stretchPoints(pointList, pointFactor, ns, np, neList, neList.get(0));
if (newRule == null) return null;
// add the rule to the global list
ns.msg = Info.getValueOnNode(ns.node);
if (ns.msg != null && ns.msg.length() == 0) ns.msg = null;
ns.values = newRule;
// stop now if a highlight or port object
if (ns.layer == null || ns.layer == Generic.tech().portNode) continue;
NodeInfo.LayerDetails nild = new NodeInfo.LayerDetails();
nodeLayersList.add(nild);
nild.layer = giLayer;
nild.ns = ns;
nild.style = getStyle(ns.node);
nild.representation = Technology.NodeLayer.POINTS;
nild.values = fixValues(np, ns.values);
nild.inNodes = null;
if (nild.values.length == 2)
{
if (nild.style == Poly.Type.CROSSED ||
nild.style == Poly.Type.FILLED ||
nild.style == Poly.Type.CLOSED)
{
nild.representation = Technology.NodeLayer.BOX;
// if (minFactor != 0)
// nodeLayers[count].representation = Technology.NodeLayer.MINBOX;
}
}
// add in variations
if (variations.size() > 0)
{
nild.inNodes = new BitSet();
nild.inNodes.set(0);
int variationIndex = 1;
for(Example variation : variations)
{
// align variation with main example and determine overall size
Sample namedSample = null;
for(Sample s : variation.samples)
if (!s.node.getNameKey().isTempname()) { namedSample = s; break; }
Sample mainSample = null;
for(Sample s : neList.get(0).samples)
if (s.layer == namedSample.layer) { mainSample = s; break; }
double lX = neList.get(0).lx - mainSample.xPos + namedSample.xPos;
double hX = neList.get(0).hx - mainSample.xPos + namedSample.xPos;
double lY = neList.get(0).ly - mainSample.yPos + namedSample.yPos;
double hY = neList.get(0).hy - mainSample.yPos + namedSample.yPos;
Sample samp = null;
for(Sample s : variation.samples)
if (s.layer == ns.layer) { samp = s; break; }
if (samp != null)
{
NodeInfo.LayerDetails variationLayer = new NodeInfo.LayerDetails();
nodeLayersList.add(variationLayer);
variationLayer.layer = giLayer;
variationLayer.ns = samp;
variationLayer.style = getStyle(samp.node);
// rectangular sample: get the bounding box in (px, py) TODO: get this right
Rectangle2D variationBounds = getBoundingBox(samp.node);
Technology.TechPoint [] variationRule = new Technology.TechPoint[2];
EdgeH horiz = EdgeH.fromLeft(variationBounds.getMinX()-lX);
EdgeV vert = EdgeV.fromBottom(variationBounds.getMinY()-lY);
variationRule[0] = new Technology.TechPoint(horiz, vert);
horiz = EdgeH.fromRight(hX-variationBounds.getMaxX());
vert = EdgeV.fromTop(hY-variationBounds.getMaxY());
variationRule[1] = new Technology.TechPoint(horiz, vert);
variationLayer.values = fixValues(np, variationRule);
variationLayer.representation = Technology.NodeLayer.BOX;
variationLayer.inNodes = new BitSet();
variationLayer.inNodes.set(variationIndex);
}
variationIndex++;
}
}
}
NodeInfo.LayerDetails [] nodeLayers = new NodeInfo.LayerDetails[nodeLayersList.size()];
for(int i=0; i<nodeLayersList.size(); i++)
nodeLayers[i] = nodeLayersList.get(i);
return nodeLayers;
}
private NodeInfo.LayerDetails [] makeNodeScaledUniformly(List<Example> neList, Cell np, LayerInfo [] lis,
Example variation, NodeInfo.LayerDetails [] variationDetails)
{
List<NodeInfo.LayerDetails> nodeLayers = new ArrayList<NodeInfo.LayerDetails>();
Example firstEx = neList.get(0);
for(Sample ns : firstEx.samples)
{
if (ns.layer != null && ns.layer != Generic.tech().portNode)
{
LayerInfo cutLayer = null;
String cutLayerName = ns.layer.getName().substring(6);
for(int i=0; i<lis.length; i++)
{
if (cutLayerName.equals(lis[i].name) && lis[i].fun.isContact()) { cutLayer = lis[i]; break; }
}
if (cutLayer != null)
{
// this is the contact layer
boolean copied = false;
for(int i=0; i<variationDetails.length; i++)
{
if (variationDetails[i].layer == cutLayer)
{
if (variationDetails[i].multiCut)
{
// just copy this layer
nodeLayers.add(variationDetails[i]);
copied = true;
break;
}
}
}
if (copied) continue;
}
}
Sample samp = ns;
if (variation != null)
{
for(Sample s : variation.samples)
{
if (s.layer == ns.layer) { samp = s; break; }
}
}
Rectangle2D nodeBounds = getBoundingBox(samp.node);
AffineTransform trans = samp.node.rotateOut();
// see if there is polygonal information
Point2D [] pointList = null;
int [] pointFactor = null;
Point2D [] points = null;
if (samp.node.getProto() == Artwork.tech().filledPolygonNode ||
samp.node.getProto() == Artwork.tech().closedPolygonNode ||
samp.node.getProto() == Artwork.tech().openedPolygonNode ||
samp.node.getProto() == Artwork.tech().openedDottedPolygonNode ||
samp.node.getProto() == Artwork.tech().openedDashedPolygonNode ||
samp.node.getProto() == Artwork.tech().openedThickerPolygonNode)
{
points = samp.node.getTrace();
}
if (points != null)
{
// fill the array
pointList = new Point2D[points.length];
pointFactor = new int[points.length];
for(int i=0; i<points.length; i++)
{
pointList[i] = new Point2D.Double(nodeBounds.getCenterX() + points[i].getX(),
nodeBounds.getCenterY() + points[i].getY());
trans.transform(pointList[i], pointList[i]);
pointFactor[i] = RATIOCENTX|RATIOCENTY;
}
} else
{
// see if it is an arc of a circle
double [] angles = null;
if (samp.node.getProto() == Artwork.tech().circleNode || samp.node.getProto() == Artwork.tech().thickCircleNode)
{
angles = samp.node.getArcDegrees();
if (angles[0] == 0 && angles[1] == 0) angles = null;
}
// set sample description
if (angles != null)
{
// handle circular arc sample
pointList = new Point2D[3];
pointFactor = new int[3];
pointList[0] = new Point2D.Double(nodeBounds.getCenterX(), nodeBounds.getCenterY());
double dist = nodeBounds.getMaxX() - nodeBounds.getCenterX();
pointList[1] = new Point2D.Double(nodeBounds.getCenterX() + dist * Math.cos(angles[0]),
nodeBounds.getCenterY() + dist * Math.sin(angles[0]));
trans.transform(pointList[1], pointList[1]);
pointFactor[0] = FROMCENTX|FROMCENTY;
pointFactor[1] = RATIOCENTX|RATIOCENTY;
pointFactor[2] = RATIOCENTX|RATIOCENTY;
} else if (samp.node.getProto() == Artwork.tech().circleNode || samp.node.getProto() == Artwork.tech().thickCircleNode ||
samp.node.getProto() == Artwork.tech().filledCircleNode)
{
// handle circular sample
pointList = new Point2D[2];
pointFactor = new int[2];
pointList[0] = new Point2D.Double(nodeBounds.getCenterX(), nodeBounds.getCenterY());
pointList[1] = new Point2D.Double(nodeBounds.getMaxX(), nodeBounds.getCenterY());
pointFactor[0] = FROMCENTX|FROMCENTY;
pointFactor[1] = TOEDGERIGHT|FROMCENTY;
} else
{
// rectangular sample: get the bounding box in (px, py)
pointList = new Point2D[2];
pointFactor = new int[2];
pointList[0] = new Point2D.Double(nodeBounds.getMinX(), nodeBounds.getMinY());
pointList[1] = new Point2D.Double(nodeBounds.getMaxX(), nodeBounds.getMaxY());
// preset stretch factors to go to the edges of the box
pointFactor[0] = TOEDGELEFT|TOEDGEBOT;
pointFactor[1] = TOEDGERIGHT|TOEDGETOP;
}
}
// add the rule to the collection
Technology.TechPoint [] newRule = stretchPoints(pointList, pointFactor, samp, np, neList, samp.parent);
if (newRule == null) return null;
samp.msg = Info.getValueOnNode(samp.node);
if (samp.msg != null && samp.msg.length() == 0) samp.msg = null;
samp.values = newRule;
// stop now if a highlight or port object
if (samp.layer == null || samp.layer == Generic.tech().portNode) continue;
// determine the layer
LayerInfo layer = null;
String desiredLayer = samp.layer.getName().substring(6);
for(int i=0; i<lis.length; i++)
{
if (desiredLayer.equals(lis[i].name)) { layer = lis[i]; break; }
}
if (layer == null)
{
error.markError(samp.node, np, "Unknown layer: " + desiredLayer);
return null;
}
NodeInfo.LayerDetails nld = new NodeInfo.LayerDetails();
nodeLayers.add(nld);
nld.layer = layer;
nld.ns = samp;
nld.style = getStyle(samp.node);
nld.representation = Technology.NodeLayer.POINTS;
nld.values = fixValues(np, samp.values);
if (nld.values.length == 2)
{
if (nld.style == Poly.Type.CROSSED ||
nld.style == Poly.Type.FILLED ||
nld.style == Poly.Type.CLOSED)
{
nld.representation = Technology.NodeLayer.BOX;
// Variable var2 = ns.node.getVar(Info.MINSIZEBOX_KEY);
// if (var2 != null) nld.representation = Technology.NodeLayer.MINBOX;
}
}
}
NodeInfo.LayerDetails[] nodeLayerArray = new NodeInfo.LayerDetails[nodeLayers.size()];
for(int i=0; i<nodeLayers.size(); i++)
nodeLayerArray[i] = nodeLayers.get(i);
return nodeLayerArray;
}
/**
* Method to build a rule for multiple contact-cut sample "ns" from the
* overall example list in "neList". Returns true on error.
*/
private NodeInfo.LayerDetails getMultiCutRule(Sample ns, List<Example> neList, Cell np)
{
// find the highlight layer
Example firstEx = neList.get(0);
Sample hs = needHighlightLayer(firstEx, np);
if (hs == null) return null;
Rectangle2D highlightBounds = hs.node.getBounds();
// determine size of each cut
Rectangle2D nodeBounds = ns.node.getBounds();
double multiXS = DBMath.round(nodeBounds.getWidth());
double multiYS = DBMath.round(nodeBounds.getHeight());
// determine indentation of cuts
double leftIndent = DBMath.round(nodeBounds.getMinX() - highlightBounds.getMinX());
double rightIndent = DBMath.round(highlightBounds.getMaxX() - nodeBounds.getMaxX());
double topIndent = DBMath.round(highlightBounds.getMaxY() - nodeBounds.getMaxY());
double bottomIndent = DBMath.round(nodeBounds.getMinY() - highlightBounds.getMinY());
double realIndentX = nodeBounds.getMinX() - firstEx.lx + multiXS/2;
double realIndentY = nodeBounds.getMinY() - firstEx.ly + multiYS/2;
if (rightIndent != leftIndent || bottomIndent != leftIndent || topIndent != leftIndent)
{
error.markError(ns.node, np, "Multiple contact cuts must be indented uniformly (left=" +
leftIndent + ", right=" + rightIndent +
", top=" + topIndent + ", bottom=" + bottomIndent + ")");
return null;
}
double multiIndent = leftIndent;
// look at every example after the first
double xSep = -1, ySep = -1;
for(int n=1; n<neList.size(); n++)
{
Example ne = neList.get(n);
// count number of samples equivalent to the main sample
int total = 0;
for(Sample nso : ne.samples)
{
if (nso.assoc == ns)
{
// make sure size is proper
Rectangle2D oNodeBounds = nso.node.getBounds();
if (multiXS != oNodeBounds.getWidth() || multiYS != oNodeBounds.getHeight())
{
error.markError(nso.node, np, "Multiple contact cuts must not differ in size");
return null;
}
total++;
}
}
// allocate space for these samples
Sample [] nsList = new Sample[total];
// fill the list of samples
int fill = 0;
for(Sample nso : ne.samples)
{
if (nso.assoc == ns) nsList[fill++] = nso;
}
// analyze the samples for separation
for(int i=1; i<total; i++)
{
// find separation
Rectangle2D thisNodeBounds = nsList[i].node.getBounds();
Rectangle2D lastNodeBounds = nsList[i-1].node.getBounds();
double sepX = DBMath.round(Math.abs(lastNodeBounds.getCenterX() - thisNodeBounds.getCenterX()));
double sepY = DBMath.round(Math.abs(lastNodeBounds.getCenterY() - thisNodeBounds.getCenterY()));
// check for validity
if (sepX < multiXS && sepY < multiYS)
{
error.markError(nsList[i].node, np, "Multiple contact cuts must not overlap");
return null;
}
// accumulate minimum separation
if (sepX >= multiXS)
{
if (xSep < 0) xSep = sepX; else
{
if (xSep > sepX) xSep = sepX;
}
}
if (sepY >= multiYS)
{
if (ySep < 0) ySep = sepY; else
{
if (ySep > sepY) ySep = sepY;
}
}
}
// finally ensure that all separations are multiples of "multiSep"
for(int i=1; i<total; i++)
{
// find X separation
Rectangle2D thisNodeBounds = nsList[i].node.getBounds();
Rectangle2D lastNodeBounds = nsList[i-1].node.getBounds();
double sepX = Math.abs(lastNodeBounds.getCenterX() - thisNodeBounds.getCenterX());
double sepY = Math.abs(lastNodeBounds.getCenterY() - thisNodeBounds.getCenterY());
if (sepX / xSep * xSep != sepX)
{
error.markError(nsList[i].node, np, "Multiple contact cut X spacing must be uniform");
return null;
}
// find Y separation
if (sepY / ySep * ySep != sepY)
{
error.markError(nsList[i].node, np, "Multiple contact cut Y spacing must be uniform");
return null;
}
}
}
double multiSepX = xSep - multiXS;
double multiSepY = ySep - multiYS;
if (multiSepX != multiSepY)
{
error.markError(null, np, "Multiple contact cut X and Y spacing must be the same (X=" + multiSepX + ", Y=" + multiSepY + ")");
return null;
}
ns.values = new Technology.TechPoint[2];
ns.values[0] = new Technology.TechPoint(EdgeH.fromLeft(realIndentX), EdgeV.fromBottom(realIndentY));
ns.values[1] = new Technology.TechPoint(EdgeH.fromRight(realIndentX), EdgeV.fromTop(realIndentY));
NodeInfo.LayerDetails multiDetails = new NodeInfo.LayerDetails();
multiDetails.style = getStyle(ns.node);
multiDetails.representation = Technology.NodeLayer.POINTS;
if (multiDetails.style == Poly.Type.CROSSED ||
multiDetails.style == Poly.Type.FILLED ||
multiDetails.style == Poly.Type.CLOSED)
{
multiDetails.representation = Technology.NodeLayer.BOX;
// Variable var2 = ns.node.getVar(Info.MINSIZEBOX_KEY);
// if (var2 != null) multiDetails.representation = Technology.NodeLayer.MINBOX;
}
multiDetails.values = ns.values;
multiDetails.ns = ns;
multiDetails.multiCut = true;
multiDetails.representation = Technology.NodeLayer.MULTICUTBOX;
multiDetails.multiXS = multiXS;
multiDetails.multiYS = multiYS;
multiDetails.multiIndent = multiIndent;
multiDetails.multiSep = multiSepX;
multiDetails.multiSep2D = multiSepX;
return multiDetails;
}
/**
* Method to adjust the "count"-long array of points in "px" and "py" according
* to the stretch factor bits in "factor" and return an array that describes
* these points. Returns zero on error.
*/
private Technology.TechPoint [] stretchPoints(Point2D [] pts, int [] factor,
Sample ns, Cell np, List<Example> neList, Example firstEx)
{
Technology.TechPoint [] newRule = new Technology.TechPoint[pts.length];
for(int i=0; i<pts.length; i++)
{
// determine the X algorithm
EdgeH horiz = null;
if ((factor[i]&TOEDGELEFT) != 0)
{
// left edge rule
horiz = EdgeH.fromLeft(pts[i].getX()-firstEx.lx);
} else if ((factor[i]&TOEDGERIGHT) != 0)
{
// right edge rule
horiz = EdgeH.fromRight(firstEx.hx-pts[i].getX());
} else if ((factor[i]&FROMCENTX) != 0)
{
// center rule
horiz = EdgeH.fromCenter(pts[i].getX()-(firstEx.lx+firstEx.hx)/2);
} else if ((factor[i]&RATIOCENTX) != 0)
{
// constant stretch rule
if (firstEx.hx == firstEx.lx)
{
horiz = EdgeH.makeCenter();
} else
{
horiz = new EdgeH((pts[i].getX()-(firstEx.lx+firstEx.hx)/2) / (firstEx.hx-firstEx.lx), 0);
}
} else
{
error.markStretchProblem(neList, ns, np, pts[i].getX(), true);
return null;
}
// determine the Y algorithm
EdgeV vert = null;
if ((factor[i]&TOEDGEBOT) != 0)
{
// bottom edge rule
vert = EdgeV.fromBottom(pts[i].getY()-firstEx.ly);
} else if ((factor[i]&TOEDGETOP) != 0)
{
// top edge rule
vert = EdgeV.fromTop(firstEx.hy-pts[i].getY());
} else if ((factor[i]&FROMCENTY) != 0)
{
// center rule
vert = EdgeV.fromCenter(pts[i].getY()-(firstEx.ly+firstEx.hy)/2);
} else if ((factor[i]&RATIOCENTY) != 0)
{
// constant stretch rule
if (firstEx.hy == firstEx.ly)
{
vert = EdgeV.makeCenter();
} else
{
vert = new EdgeV((pts[i].getY()-(firstEx.ly+firstEx.hy)/2) / (firstEx.hy-firstEx.ly), 0);
}
} else
{
error.markStretchProblem(neList, ns, np, pts[i].getY(), false);
return null;
}
newRule[i] = new Technology.TechPoint(horiz, vert);
}
return newRule;
}
private Technology.TechPoint [] fixValues(NodeProto np, Technology.TechPoint [] currentList)
{
EdgeH h1 = null, h2 = null, h3 = null;
EdgeV v1 = null, v2 = null, v3 = null;
for(int p=0; p<currentList.length; p++)
{
EdgeH h = currentList[p].getX();
if (h.equals(h1) || h.equals(h2) || h.equals(h3)) continue;
if (h1 == null) h1 = h; else
if (h2 == null) h2 = h; else
h3 = h;
EdgeV v = currentList[p].getY();
if (v.equals(v1) || v.equals(v2) || v.equals(v3)) continue;
if (v1 == null) v1 = v; else
if (v2 == null) v2 = v; else
v3 = v;
}
if (h1 != null && h2 != null && h3 == null &&
v1 != null && v2 != null && v3 == null)
{
// reduce to a box with two points
currentList = new Technology.TechPoint[2];
currentList[0] = new Technology.TechPoint(h1, v1);
currentList[1] = new Technology.TechPoint(h2, v2);
}
return currentList;
}
/************************************* SUPPORT *************************************/
/**
* Method to associate the samples of example "neList" in cell "np"
* Returns true if there is an error
*/
private boolean associateExamples(List<Example> neList, Cell np)
{
// if there is only one example, no association
if (neList.size() <= 1) return false;
// associate each example "ne" with the original in "neList"
Example firstEx = neList.get(0);
for(int n=1; n<neList.size(); n++)
{
Example ne = neList.get(n);
// clear associations for every sample "ns" in the example "ne"
for(Sample ns : ne.samples)
ns.assoc = null;
// associate every sample "ns" in the example "ne"
for(Sample ns : ne.samples)
{
if (ns.assoc != null) continue;
// cannot have center in other examples
if (ns.layer == Generic.tech().cellCenterNode)
{
error.markError(ns.node, np, "Grab point should only be in main example");
return true;
}
// count number of similar layers in original example "neList"
int total = 0;
Sample nsFound = null;
for(Sample nsList : firstEx.samples)
{
if (nsList.layer != ns.layer) continue;
total++;
nsFound = nsList;
}
// no similar layer found in the original: error
if (total == 0)
{
error.markError(ns.node, np, "Layer " + Info.getSampleName(ns.layer) + " not found in main example");
return true;
}
// just one in the original: simple association
if (total == 1)
{
ns.assoc = nsFound;
continue;
}
// if it is a port, associate by port name
if (ns.layer == Generic.tech().portNode)
{
String name = Info.getPortName(ns.node);
if (name == null)
{
error.markError(ns.node, np, "Port does not have a name");
return true;
}
// search the original for that port
boolean found = false;
for(Sample nsList : firstEx.samples)
{
if (nsList.layer == Generic.tech().portNode)
{
String otherName = Info.getPortName(nsList.node);
if (otherName == null)
{
error.markError(nsList.node, np, "Port does not have a name");
return true;
}
if (!name.equalsIgnoreCase(otherName)) continue;
ns.assoc = nsList;
found = true;
break;
}
}
if (!found)
{
error.markError(null, np, "Could not find port " + name + " in all examples");
return true;
}
continue;
}
// count the number of this layer in example "ne"
int i = 0;
for(Sample nsList : ne.samples)
{
if (nsList.layer == ns.layer) i++;
}
// if number of similar layers differs: error
if (total != i)
{
error.markError(ns.node, np, "Layer " + Info.getSampleName(ns.layer) +
" found " + total + " times in main example, " + i + " in others");
return true;
}
// make a list of samples on this layer in original
List<Sample> mainList = new ArrayList<Sample>();
i = 0;
for(Sample nsList : firstEx.samples)
{
if (nsList.layer == ns.layer) mainList.add(nsList);
}
// make a list of samples on this layer in example "ne"
List<Sample> thisList = new ArrayList<Sample>();
i = 0;
for(Sample nsList : ne.samples)
{
if (nsList.layer == ns.layer) thisList.add(nsList);
}
// sort each list in X/Y/shape
Collections.sort(mainList, new SampleCoordAscending());
Collections.sort(thisList, new SampleCoordAscending());
// see if the lists have duplication
for(i=1; i<total; i++)
{
Sample thisSample = thisList.get(i);
Sample lastSample = thisList.get(i-1);
Sample thisMainSample = mainList.get(i);
Sample lastMainSample = mainList.get(i-1);
if ((thisSample.xPos == lastSample.xPos &&
thisSample.yPos == lastSample.yPos &&
thisSample.node.getProto() == lastSample.node.getProto()) ||
(thisMainSample.xPos == lastMainSample.xPos &&
thisMainSample.yPos == lastMainSample.yPos &&
thisMainSample.node.getProto() == lastMainSample.node.getProto())) break;
}
if (i >= total)
{
// association can be made in X
for(i=0; i<total; i++)
{
Sample thisSample = thisList.get(i);
thisSample.assoc = mainList.get(i);
}
continue;
}
// don't know how to associate this sample
Sample thisSample = thisList.get(i);
error.markError(thisSample.node, np, "Sample " + Info.getSampleName(thisSample.layer) + " is unassociated");
return true;
}
// final check: make sure every sample in original example associates
for(Sample nsList : firstEx.samples)
{
nsList.assoc = null;
}
for(Sample ns : ne.samples)
{
ns.assoc.assoc = ns;
}
for(Sample nsList : firstEx.samples)
{
if (nsList.assoc == null)
{
if (nsList.layer == Generic.tech().cellCenterNode) continue;
error.markError(nsList.node, np, "Layer " + Info.getSampleName(nsList.layer) + " found in main example, but not others");
return true;
}
}
}
return false;
}
private static class SamplesByLayerOrder implements Comparator<Sample>
{
private LayerInfo [] lList;
SamplesByLayerOrder(LayerInfo [] lList) { this.lList = lList; }
public int compare(Sample s1, Sample s2)
{
int i1 = -1;
if (s1.layer != null && s1.layer != Generic.tech().portNode)
{
String s1Name = s1.layer.getName().substring(6);
for(int i = 0; i < lList.length; i++) if (lList[i].name.equals(s1Name)) { i1 = i; break; }
}
int i2 = -1;
if (s2.layer != null && s2.layer != Generic.tech().portNode)
{
String s2Name = s2.layer.getName().substring(6);
for(int i = 0; i < lList.length; i++) if (lList[i].name.equals(s2Name)) { i2 = i; break; }
}
return i1-i2;
}
}
private static class PortsByAngleAndName implements Comparator<NodeInfo.PortDetails>
{
public int compare(NodeInfo.PortDetails s1, NodeInfo.PortDetails s2)
{
if (s1.angle != s2.angle) return s1.angle - s2.angle;
return s1.name.compareTo(s2.name);
}
}
private static class SampleCoordAscending implements Comparator<Sample>
{
public int compare(Sample s1, Sample s2)
{
if (s1.xPos != s2.xPos) return (int)(s1.xPos - s2.xPos);
if (s1.yPos != s2.yPos) return (int)(s1.yPos - s2.yPos);
return s1.node.getName().compareTo(s2.node.getName());
}
}
/* flags about the edge positions in the examples */
private static final int TOEDGELEFT = 01; /* constant to left edge */
private static final int TOEDGERIGHT = 02; /* constant to right edge */
private static final int TOEDGETOP = 04; /* constant to top edge */
private static final int TOEDGEBOT = 010; /* constant to bottom edge */
private static final int FROMCENTX = 020; /* constant in X to center */
private static final int FROMCENTY = 040; /* constant in Y to center */
private static final int RATIOCENTX = 0100; /* fixed ratio from X center to edge */
private static final int RATIOCENTY = 0200; /* fixed ratio from Y center to edge */
private Poly.Type getStyle(NodeInst ni)
{
// layer style
Poly.Type sty = null;
if (ni.getProto() == Artwork.tech().filledBoxNode) sty = Poly.Type.FILLED; else
if (ni.getProto() == Artwork.tech().boxNode) sty = Poly.Type.CLOSED; else
if (ni.getProto() == Artwork.tech().crossedBoxNode) sty = Poly.Type.CROSSED; else
if (ni.getProto() == Artwork.tech().filledPolygonNode) sty = Poly.Type.FILLED; else
if (ni.getProto() == Artwork.tech().closedPolygonNode) sty = Poly.Type.CLOSED; else
if (ni.getProto() == Artwork.tech().openedPolygonNode) sty = Poly.Type.OPENED; else
if (ni.getProto() == Artwork.tech().openedDottedPolygonNode) sty = Poly.Type.OPENEDT1; else
if (ni.getProto() == Artwork.tech().openedDashedPolygonNode) sty = Poly.Type.OPENEDT2; else
if (ni.getProto() == Artwork.tech().openedThickerPolygonNode) sty = Poly.Type.OPENEDT3; else
if (ni.getProto() == Artwork.tech().filledCircleNode) sty = Poly.Type.DISC; else
if (ni.getProto() == Artwork.tech().circleNode)
{
sty = Poly.Type.CIRCLE;
double [] angles = ni.getArcDegrees();
if (angles[0] != 0 || angles[1] != 0) sty = Poly.Type.CIRCLEARC;
} else if (ni.getProto() == Artwork.tech().thickCircleNode)
{
sty = Poly.Type.THICKCIRCLE;
double [] angles = ni.getArcDegrees();
if (angles[0] != 0 || angles[1] != 0) sty = Poly.Type.THICKCIRCLEARC;
} else if (ni.getProto() == Generic.tech().invisiblePinNode)
{
Variable var = ni.getVar(Artwork.ART_MESSAGE);
if (var != null)
{
TextDescriptor.Position pos = var.getTextDescriptor().getPos();
if (pos == TextDescriptor.Position.BOXED) sty = Poly.Type.TEXTBOX; else
if (pos == TextDescriptor.Position.CENT) sty = Poly.Type.TEXTCENT; else
if (pos == TextDescriptor.Position.UP) sty = Poly.Type.TEXTBOT; else
if (pos == TextDescriptor.Position.DOWN) sty = Poly.Type.TEXTTOP; else
if (pos == TextDescriptor.Position.LEFT) sty = Poly.Type.TEXTRIGHT; else
if (pos == TextDescriptor.Position.RIGHT) sty = Poly.Type.TEXTLEFT; else
if (pos == TextDescriptor.Position.UPLEFT) sty = Poly.Type.TEXTBOTRIGHT; else
if (pos == TextDescriptor.Position.UPRIGHT) sty = Poly.Type.TEXTBOTLEFT; else
if (pos == TextDescriptor.Position.DOWNLEFT) sty = Poly.Type.TEXTTOPRIGHT; else
if (pos == TextDescriptor.Position.DOWNRIGHT) sty = Poly.Type.TEXTTOPLEFT;
}
}
if (sty == null)
{
System.out.println("Warning: Cannot determine style to use for " + ni.describe(false) +
" node in " + ni.getParent() + ", assuming FILLED");
sty = Poly.Type.FILLED;
}
return sty;
}
/**
* Method to return the actual bounding box of layer node "ni" in the
* reference variables "lx", "hx", "ly", and "hy"
*/
private Rectangle2D getBoundingBox(NodeInst ni)
{
Rectangle2D bounds = ni.getBaseShape().getBounds2D();
// Rectangle2D bounds = ni.getBounds();
// if (ni.getProto() == Generic.tech().portNode)
// {
// double portShrink = 2;
// bounds.setRect(bounds.getMinX() + portShrink, bounds.getMinY() + portShrink,
// bounds.getWidth()-portShrink*2, bounds.getHeight()-portShrink*2);
// }
bounds.setRect(DBMath.round(bounds.getMinX()), DBMath.round(bounds.getMinY()),
DBMath.round(bounds.getWidth()), DBMath.round(bounds.getHeight()));
return bounds;
}
private Sample needHighlightLayer(Example neList, Cell np)
{
// find the highlight layer
for(Sample ns : neList.samples)
{
if (ns.layer == null) return ns;
}
error.markError(null, np, "No highlight layer on contact");
return null;
}
/************************************* WRITE TECHNOLOGY AS "XML" *************************************/
/**
* Method to convert tech-edit information to an Xml.Technology.
* @param newTechName new technology name.
* @param gi general technology information.
* @param lList information about layers.
* @param nList information about primitive nodes.
* @param aList information about primitive arcs.
*/
private Xml.Technology makeXml(String newTechName, GeneralInfo gi, LayerInfo[] lList, NodeInfo[] nList, ArcInfo[] aList)
{
Xml.Technology t = new Xml.Technology();
t.techName = newTechName;
t.shortTechName = gi.shortName;
t.description = gi.description;
t.minNumMetals = t.maxNumMetals = t.defaultNumMetals = gi.defaultNumMetals;
t.scaleValue = gi.scale;
t.scaleRelevant = gi.scaleRelevant;
t.resolutionValue = gi.resolution;
t.defaultFoundry = gi.defaultFoundry;
t.minResistance = gi.minRes;
t.minCapacitance = gi.minCap;
if (gi.transparentColors != null) {
for (int i = 0; i < gi.transparentColors.length; i++)
t.transparentLayers.add(gi.transparentColors[i]);
}
for (LayerInfo li: lList) {
if (li.pseudo) continue;
Xml.Layer layer = new Xml.Layer();
layer.name = li.name;
layer.function = li.fun;
layer.extraFunction = li.funExtra;
layer.desc = li.desc;
layer.thick3D = li.thick3d;
layer.height3D = li.height3d;
layer.cif = li.cif;
layer.resistance = li.spiRes;
layer.capacitance = li.spiCap;
layer.edgeCapacitance = li.spiECap;
if (li.pureLayerNode != null) {
layer.pureLayerNode = new Xml.PureLayerNode();
layer.pureLayerNode.name = li.pureLayerNode.name;
layer.pureLayerNode.style = li.pureLayerNode.nodeLayers[0].style;
layer.pureLayerNode.port = li.pureLayerNode.nodePortDetails[0].name;
layer.pureLayerNode.size.addLambda(DBMath.round(li.pureLayerNode.xSize));
for (ArcInfo a: li.pureLayerNode.nodePortDetails[0].connections)
layer.pureLayerNode.portArcs.add(a.name);
}
t.layers.add(layer);
}
for (ArcInfo ai: aList) {
Xml.ArcProto ap = new Xml.ArcProto();
ap.name = ai.name;
ap.function = ai.func;
ap.wipable = ai.wipes;
ap.curvable = ai.curvable;
ap.special = ai.special;
ap.notUsed = ai.notUsed;
ap.skipSizeInPalette = ai.skipSizeInPalette;
ap.extended = !ai.noExtend;
ap.fixedAngle = ai.fixAng;
ap.angleIncrement = ai.angInc;
ap.antennaRatio = ai.antennaRatio;
for (ArcInfo.LayerDetails al: ai.arcDetails) {
Xml.ArcLayer l = new Xml.ArcLayer();
l.layer = al.layer.name;
l.style = al.style == Poly.Type.FILLED ? Poly.Type.FILLED : Poly.Type.CLOSED;
l.extend.addLambda(DBMath.round(al.width/2));
ap.arcLayers.add(l);
}
t.arcs.add(ap);
}
for (NodeInfo ni: nList) {
if (ni.func == PrimitiveNode.Function.NODE && ni.nodeLayers[0].layer.pureLayerNode == ni)
continue;
Xml.PrimitiveNodeGroup png = new Xml.PrimitiveNodeGroup();
if (ni.primitiveNodeGroupNames == null)
{
Xml.PrimitiveNode n = new Xml.PrimitiveNode();
n.name = ni.name;
n.function = ni.func;
n.lowVt = ni.lowVt;
n.highVt = ni.highVt;
n.nativeBit = ni.nativeBit;
n.od18 = ni.od18;
n.od25 = ni.od25;
n.od33 = ni.od33;
png.nodes.add(n);
} else
{
for(String s : ni.primitiveNodeGroupNames) {
Xml.PrimitiveNode n = new Xml.PrimitiveNode();
n.name = s;
n.function = ni.func;
n.lowVt = ni.lowVt;
n.highVt = ni.highVt;
n.nativeBit = ni.nativeBit;
n.od18 = ni.od18;
n.od25 = ni.od25;
n.od33 = ni.od33;
png.nodes.add(n);
}
}
png.shrinkArcs = ni.arcsShrink;
png.square = ni.square;
png.canBeZeroSize = ni.canBeZeroSize;
png.wipes = ni.wipes;
png.lockable = ni.lockable;
png.edgeSelect = ni.edgeSelect;
png.skipSizeInPalette = ni.skipSizeInPalette;
png.notUsed = ni.notUsed;
EPoint minFullSize = EPoint.fromLambda(0.5*ni.xSize, 0.5*ni.ySize);
// pn.sizeOffset = ni.so;
// double lx = -minFullSize.getLambdaX();
// double hx = minFullSize.getLambdaX();
// double ly = -minFullSize.getLambdaY();
// double hy = minFullSize.getLambdaY();
// if (ni.so != null) {
// lx += ni.so.getLowXOffset();
// hx -= ni.so.getHighXOffset();
// ly += ni.so.getLowYOffset();
// hy -= ni.so.getHighYOffset();
// }
/* pn.nodeBase = ERectangle.fromLambda(lx, ly, hx - lx, hy - ly);
if (!minFullSize.equals(EPoint.ORIGIN))
pn.diskOffset = minFullSize;*/
// EPoint p2 = EPoint.fromGrid(pn.nodeBase.getGridWidth() >> 1, pn.nodeBase.getGridHeight() >> 1);
// if (!p2.equals(minFullSize))
// pn.diskOffset.put(Integer.valueOf(1), minFullSize);
// if (!p2.equals(EPoint.ORIGIN))
// pn.diskOffset.put(Integer.valueOf(2), p2);
// pn.defaultWidth.value = DBMath.round(ni.xSize);
// pn.defaultHeight.value = DBMath.round(ni.ySize);
if (ni.spiceTemplate != null && !ni.spiceTemplate.equals(""))
png.spiceTemplate = ni.spiceTemplate;
for(int j=0; j<ni.nodeLayers.length; j++) {
NodeInfo.LayerDetails nl = ni.nodeLayers[j];
png.nodeLayers.add(makeNodeLayerDetails(nl, ni.serp, minFullSize));
}
for (int j = 0; j < ni.nodePortDetails.length; j++) {
NodeInfo.PortDetails pd = ni.nodePortDetails[j];
Xml.PrimitivePort pp = new Xml.PrimitivePort();
pp.name = pd.name;
pp.portAngle = pd.angle;
pp.portRange = pd.range;
pp.portTopology = pd.netIndex;
EdgeH left = pd.values[0].getX();
EdgeH right = pd.values[1].getX();
EdgeV bottom = pd.values[0].getY();
EdgeV top = pd.values[1].getY();
pp.lx.k = left.getMultiplier()*2;
pp.lx.addLambda(left.getAdder() + minFullSize.getLambdaX()*left.getMultiplier()*2);
pp.hx.k = right.getMultiplier()*2;
pp.hx.addLambda(right.getAdder() + minFullSize.getLambdaX()*right.getMultiplier()*2);
pp.ly.k = bottom.getMultiplier()*2;
pp.ly.addLambda(bottom.getAdder() + minFullSize.getLambdaY()*bottom.getMultiplier()*2);
pp.hy.k = top.getMultiplier()*2;
pp.hy.addLambda(top.getAdder() + minFullSize.getLambdaY()*top.getMultiplier()*2);
for (ArcInfo a: pd.connections)
pp.portArcs.add(a.name);
png.ports.add(pp);
}
png.specialType = ni.specialType;
if (png.specialType == PrimitiveNode.SERPTRANS) {
png.specialValues = new double[6];
for (int i = 0; i < 6; i++)
png.specialValues[i] = ni.specialValues[i];
}
if (ni.nodeSizeRule != null) {
png.nodeSizeRule = new Xml.NodeSizeRule();
png.nodeSizeRule.width = ni.nodeSizeRule.getWidth();
png.nodeSizeRule.height = ni.nodeSizeRule.getHeight();
png.nodeSizeRule.rule = ni.nodeSizeRule.getRuleName();
}
ERectangle base = calcBaseRectangle(ni.so, png.nodeLayers, ni.nodeSizeRule);
png.baseLX.value = base.getLambdaMinX();
png.baseHX.value = base.getLambdaMaxX();
png.baseLY.value = base.getLambdaMinY();
png.baseHY.value = base.getLambdaMaxY();
t.nodeGroups.add(png);
}
addSpiceHeader(t, 1, gi.spiceLevel1Header);
addSpiceHeader(t, 2, gi.spiceLevel2Header);
addSpiceHeader(t, 3, gi.spiceLevel3Header);
t.menuPalette = gi.menuPalette;
Xml.Foundry foundry = new Xml.Foundry();
foundry.name = gi.defaultFoundry;
for (LayerInfo li: lList) {
if (li.gds != null && li.gds.length() > 0)
foundry.layerGds.put(li.name, li.gds);
}
if (gi.conDist != null && gi.unConDist != null) {
int layerTotal = lList.length;
int ruleIndex = 0;
for (int i1 = 0; i1 < layerTotal; i1++) {
LayerInfo l1 = lList[i1];
for (int i2 = i1; i2 < layerTotal; i2++) {
LayerInfo l2 = lList[i2];
double conSpa = gi.conDist[ruleIndex];
double uConSpa = gi.unConDist[ruleIndex];
if (conSpa > -1)
foundry.rules.add(makeDesignRule("C" + ruleIndex, l1, l2, DRCTemplate.DRCRuleType.CONSPA, conSpa));
if (uConSpa > -1)
foundry.rules.add(makeDesignRule("U" + ruleIndex, l1, l2, DRCTemplate.DRCRuleType.UCONSPA, uConSpa));
ruleIndex++;
}
}
}
t.foundries.add(foundry);
return t;
}
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);
}
private Xml.NodeLayer makeNodeLayerDetails(NodeInfo.LayerDetails nl, boolean isSerp, EPoint correction) {
Xml.NodeLayer nld = new Xml.NodeLayer();
nld.layer = nl.layer.name;
nld.style = nl.style;
nld.portNum = nl.portIndex;
nld.inLayers = nl.inLayers;
nld.inElectricalLayers = nl.inElectricalLayers;
nld.representation = nl.representation;
Technology.TechPoint[] points = nl.values;
if (nld.representation == Technology.NodeLayer.BOX || nld.representation == Technology.NodeLayer.MULTICUTBOX) {
nld.lx.k = points[0].getX().getMultiplier()*2;
nld.lx.addLambda(DBMath.round(points[0].getX().getAdder() + correction.getLambdaX()*points[0].getX().getMultiplier()*2));
nld.hx.k = points[1].getX().getMultiplier()*2;
nld.hx.addLambda(DBMath.round(points[1].getX().getAdder() + correction.getLambdaX()*points[1].getX().getMultiplier()*2));
nld.ly.k = points[0].getY().getMultiplier()*2;
nld.ly.addLambda(DBMath.round(points[0].getY().getAdder() + correction.getLambdaY()*points[0].getY().getMultiplier()*2));
nld.hy.k = points[1].getY().getMultiplier()*2;
nld.hy.addLambda(DBMath.round(points[1].getY().getAdder() + correction.getLambdaY()*points[1].getY().getMultiplier()*2));
} else {
for (Technology.TechPoint p: points)
nld.techPoints.add(correction(p, correction));
}
nld.sizex = DBMath.round(nl.multiXS);
nld.sizey = DBMath.round(nl.multiYS);
nld.sep1d = DBMath.round(nl.multiSep);
nld.sep2d = DBMath.round(nl.multiSep2D);
if (isSerp) {
nld.lWidth = DBMath.round(nl.lWidth);
nld.rWidth = DBMath.round(nl.rWidth);
nld.tExtent = DBMath.round(nl.extendT);
nld.bExtent = DBMath.round(nl.extendB);
}
nld.inNodes = nl.inNodes;
return nld;
}
private Technology.TechPoint correction(Technology.TechPoint p, EPoint correction)
{
EdgeH h = p.getX();
EdgeV v = p.getY();
h = new EdgeH(h.getMultiplier(), h.getAdder() + correction.getLambdaX()*h.getMultiplier()*2);
v = new EdgeV(v.getMultiplier(), v.getAdder() + correction.getLambdaY()*v.getMultiplier()*2);
return new Technology.TechPoint(h, v);
}
private void addSpiceHeader(Xml.Technology t, int level, String[] spiceLines) {
if (spiceLines == null) return;
Xml.SpiceHeader spiceHeader = new Xml.SpiceHeader();
spiceHeader.level = level;
for (String spiceLine: spiceLines)
spiceHeader.spiceLines.add(spiceLine);
t.spiceHeaders.add(spiceHeader);
}
// private List<Object> makeMenuBoxXml(Xml.Technology t, Object o)
// {
// List<Object> menuBox = new ArrayList<Object>();
// if (o != null)
// {
// if (o instanceof List)
// {
// for(Object subO : (List)o)
// menuBox.add(subO);
// } else menuBox.add(o);
// }
// return menuBox;
// }
private DRCTemplate makeDesignRule(String ruleName, LayerInfo l1, LayerInfo l2, DRCTemplate.DRCRuleType type, double value)
{
return new DRCTemplate(ruleName, DRCTemplate.DRCMode.ALL.mode(), type, l1.name, l2.name, new double[] {value}, null, null);
}
}