All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.electric.tool.routing.Router Maven / Gradle / Ivy

/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Router.java
 *
 * Copyright (c) 2003, 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.routing;

import com.sun.electric.database.EditingPreferences;
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.Export;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.UserInterface;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.Tool;
import com.sun.electric.tool.user.CircuitChangeJobs;
import com.sun.electric.tool.user.User;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.EDimension;
import com.sun.electric.util.math.GenMath;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Parent Class for all Routers.  I really have no idea what this
 * should look like because I've never written a real router,
 * but I've started it off with a few basics.
 * 

* A Route is a List of RouteElements. See RouteElement for details * of the elements. *

* Author: Gainsley */ public abstract class Router { /** set to tell user short info on what was done */ protected boolean verbose = false; /** the tool that is making routes */ protected Tool tool; // --------------------------- Public Methods --------------------------- /** * Create the route within a Job. * @param route the route to create * @param cell the cell in which to create the route */ public void createRoute(Route route, Cell cell) { new CreateRouteJob(toString(), route, cell, verbose, tool); } /** * Method to create the route. * Does not wrap Job around it * (useful if already being called from a Job). This still * must be called from within a Job context, however. * @param route the route to create * @param cell the cell in which to create the route * @param arcsCreatedMap a map of arcs to integers which is updated to indicate the number of each arc type created. * @param nodesCreatedMap a map of nodes to integers which is updated to indicate the number of each node type created. * @param ep EditingPreferences with default sizes * @return true on error. */ public static boolean createRouteNoJob(Route route, Cell cell, Map arcsCreatedMap, Map nodesCreatedMap, EditingPreferences ep) { EDatabase.serverDatabase().checkChanging(); // check if we can edit this cell if (CircuitChangeJobs.cantEdit(cell, null, true, false, true) != 0) return true; // pass 1: build all newNodes for (RouteElement e : route) { if (e.getAction() == RouteElement.RouteElementAction.newNode) { if (e.isDone()) continue; e.doAction(ep); RouteElementPort rep = (RouteElementPort)e; Integer i = nodesCreatedMap.get(rep.getPortProto().getParent()); if (i == null) i = new Integer(0); i = new Integer(i.intValue() + 1); nodesCreatedMap.put(rep.getPortProto().getParent(), i); } } // pass 2: do all other actions (deletes, newArcs) for (RouteElement e : route) { if (e.getAction() == RouteElement.RouteElementAction.newNode) continue; ElectricObject result = e.doAction(ep); if (e.getAction() == RouteElement.RouteElementAction.newArc) { if (result == null) return true; RouteElementArc rea = (RouteElementArc)e; Integer i = arcsCreatedMap.get(rea.getArcProto()); if (i == null) i = new Integer(0); i = new Integer(i.intValue() + 1); arcsCreatedMap.put(rea.getArcProto(), i); } } if (arcsCreatedMap.get(Generic.tech().unrouted_arc) == null) { // update current unrouted arcs RouteElementPort rep = route.getStart(); if (rep != null && rep.getPortInst() != null) { PortInst pi = rep.getPortInst(); for (Iterator it = pi.getConnections(); it.hasNext(); ) { Connection conn = it.next(); ArcInst ai = conn.getArc(); if (ai.getProto() == Generic.tech().unrouted_arc) { Connection oconn = ai.getConnection(1-conn.getEndIndex()); // make new unrouted arc from end of route to arc end point, // otherwise just get rid of it if (oconn.getPortInst() != route.getEnd().getPortInst()) { RouteElementPort newEnd = RouteElementPort.existingPortInst(oconn.getPortInst(), oconn.getLocation(), ep); RouteElementArc newArc = RouteElementArc.newArc(cell, Generic.tech().unrouted_arc, Generic.tech().unrouted_arc.getDefaultLambdaBaseWidth(ep), route.getEnd(), newEnd, route.getEnd().getLocation(), oconn.getLocation(), null, ai.getTextDescriptor(ArcInst.ARC_NAME), ai, ai.isHeadExtended(), ai.isTailExtended(), null); newArc.doAction(ep); } if (conn.getArc().isLinked()) conn.getArc().kill(); } } } } return false; } public static void reportRoutingResults(String prefix, Map arcsCreatedMap, Map nodesCreatedMap, boolean beep) { List arcEntries = new ArrayList(arcsCreatedMap.keySet()); List nodeEntries = new ArrayList(nodesCreatedMap.keySet()); if (arcEntries.isEmpty() && nodeEntries.isEmpty()) { System.out.println(prefix + ": nothing added"); } else { System.out.print(prefix + " added: "); Collections.sort(arcEntries, new TextUtils.ObjectsByToString()); Collections.sort(nodeEntries, new TextUtils.ObjectsByToString()); int total = arcEntries.size() + nodeEntries.size(); int sofar = 0; for (ArcProto ap : arcEntries) { Integer i = arcsCreatedMap.get(ap); sofar++; if (sofar > 1 && total > 1) { if (sofar < total) System.out.print(", "); else System.out.print(" and "); } System.out.print(i.intValue() + " " + ap.describe()); if (i.intValue() > 1) System.out.print(" arcs"); else System.out.print(" arc"); } for (NodeProto np : nodeEntries) { Integer i = nodesCreatedMap.get(np); sofar++; if (sofar > 1 && total > 1) { if (sofar < total) System.out.print(", "); else System.out.print(" and "); } System.out.print(i.intValue() + " " + np.describe(false)); if (i.intValue() > 1) System.out.print(" nodes"); else System.out.print(" node"); } System.out.println(); if (beep) Job.getUserInterface().beep(); } } /** Method to set the tool associated with this router */ public void setTool(Tool tool) { this.tool = tool; } // -------------------------- Job to build route ------------------------ /** * Job to create the route. * Highlights the end of the Route after it creates it. */ protected static class CreateRouteJob extends Job { /** route to build */ protected Route route; /** cell in which to build route */ private Cell cell; /** port to highlight */ private PortInst portToHighlight; /** true to beep */ private boolean beep; /** Constructor */ protected CreateRouteJob(String what, Route route, Cell cell, boolean verbose, Tool tool) { super(what, tool, Job.Type.CHANGE, null, null, Job.Priority.USER); this.route = route; this.cell = cell; beep = User.isPlayClickSoundsWhenCreatingArcs(); startJob(); } @Override public boolean doIt() throws JobException { if (CircuitChangeJobs.cantEdit(cell, null, true, false, true) != 0) return false; Map arcsCreatedMap = new HashMap(); Map nodesCreatedMap = new HashMap(); createRouteNoJob(route, cell, arcsCreatedMap, nodesCreatedMap, getEditingPreferences()); portToHighlight = null; RouteElementPort finalRE = route.getEnd(); if (finalRE != null) portToHighlight = finalRE.getPortInst(); reportRoutingResults("Wiring", arcsCreatedMap, nodesCreatedMap, beep); fieldVariableChanged("portToHighlight"); return true; } @Override public void terminateOK() { if (portToHighlight != null) { UserInterface ui = Job.getUserInterface(); EditWindow_ wnd = ui.getCurrentEditWindow_(); if (wnd != null) { wnd.clearHighlighting(); wnd.addElectricObject(portToHighlight, cell); wnd.finishedHighlighting(); } } } } // ------------------------ Protected Utility Methods --------------------- /** * Determine which arc type to use to connect two ports * NOTE: for safety, will NOT return a Generic.tech.universal_arc, * Generic.tech.invisible_arc, or Generic.tech.unrouted_arc, * unless it is the currently selected arc. Will instead return null * if no other arc can be found to work. * @param port1 one end point of arc (ignored if null) * @param port2 other end point of arc (ignored if null) * @return the arc type (an ArcProto). null if none or error. */ public static ArcProto getArcToUse(PortProto port1, PortProto port2) { // current user selected arc ArcProto curAp = User.getUserTool().getCurrentArcProto(); // if connecting two busses, force a bus arc if (curAp == Schematics.tech().wire_arc && port1 != null && port2 != null) { boolean bus1 = (port1.getParent() == Schematics.tech().busPinNode) || port1.getNameKey().isBus(); boolean bus2 = (port2.getParent() == Schematics.tech().busPinNode) || port2.getNameKey().isBus(); if (bus1 && bus2) return Schematics.tech().bus_arc; } PortProto pp1 = null, pp2 = null; // Note: this makes it so either port1 or port2 can be null, // but only pp2 can be null down below if (port1 == null) pp1 = port2; else { pp1 = port1; pp2 = port2; } if (pp1 == null && pp2 == null) return null; // see if current arcproto works if (pp2 == null) { if (pp1.connectsTo(curAp)) return curAp; } else { if (pp1.connectsTo(curAp) && pp2.connectsTo(curAp)) return curAp; } // otherwise, find one that does in the current technology Technology tech = pp1.getParent().getTechnology(); ArcProto ap = findArcThatConnects(tech, pp1, pp2); if (ap != null) return ap; // none in current technology: try any technology but generic for(Iterator it = Technology.getTechnologies(); it.hasNext(); ) { Technology anyTech = it.next(); if (anyTech == tech || anyTech == Generic.tech()) continue; ap = findArcThatConnects(anyTech, pp1, pp2); if (ap != null) return ap; } return null; } private static ArcProto findArcThatConnects(Technology tech, PortProto pp1, PortProto pp2) { for(Iterator it = tech.getArcs(); it.hasNext(); ) { ArcProto ap = it.next(); if (pp1.connectsTo(ap)) { if (pp2 == null || pp2.connectsTo(ap)) return ap; } } return null; } /** * Get arc width to use to connect to a PortInst. * Uses the largest width of arc type already connected * to the port, or the default width if none found.

* You may specify the port as null, in which case it just returns * the arc's default width. * * @param obj the object to connect to, either a PortInst or an ArcInst. * @param ap the Arc prototype to connect with. * @param arcAngle the angle of the arc that will be drawn (in tenth-degrees). * @param ignoreAngle false: only arcs whose angles match arcAngle will have their * sizes used to determine the arc width. 180 degrees out of phase also matches in this case. * True: any arcs will have their sizes used to determine the return arc size. * @param ep EditingPreferences with default sizes * @return the width to use to connect */ public static double getArcWidthToUse(ElectricObject obj, ArcProto ap, int arcAngle, boolean ignoreAngle, EditingPreferences ep) { if (obj instanceof ArcInst) { ArcInst ai = (ArcInst)obj; if (ignoreAngle) return ai.getLambdaBaseWidth(); int angle = ai.getDefinedAngle(); if (angle % 1800 == arcAngle) return ai.getLambdaBaseWidth(); return ap.getDefaultLambdaBaseWidth(ep); } if (obj == null || !(obj instanceof PortInst)) return ap.getDefaultLambdaBaseWidth(ep); PortInst pi = (PortInst)obj; boolean arcFound = false; // get all ArcInsts on pi, find largest double width = ap.getDefaultLambdaBaseWidth(ep); for (Iterator it = pi.getConnections(); it.hasNext(); ) { Connection c = it.next(); ArcInst ai = c.getArc(); if (ai.getProto() != ap) continue; if (!ignoreAngle) { int angle = ai.getDefinedAngle(); if (angle % 1800 != arcAngle % 1800) continue; } // ignore default-sized arcs created by RouteElementArc.doAction() to deal with DRC errors when using fat wiring mode if (ep.isFatWires() && c.getArc().getLambdaBaseWidth() == ap.getDefaultLambdaBaseWidth(ep)) continue; double newWidth = c.getArc().getLambdaBaseWidth(); if (width < newWidth) width = newWidth; arcFound = true; } if (arcFound) return width; NodeInst ni = pi.getNodeInst(); // if still default width and node is a contact, use the width/height of the contact if (!arcFound && (ni.getProto() instanceof PrimitiveNode)) { PrimitiveNode pn = (PrimitiveNode)ni.getProto(); if (ep.isFatWires()) { // if a contact or substrate tap or well tap if (pn.getFunction().isContact() || pn.getFunction() == PrimitiveNode.Function.SUBSTRATE || pn.getFunction() == PrimitiveNode.Function.WELL) { // size calls take into account rotation double xsize = ni.getXSizeWithoutOffset(); double ysize = ni.getYSizeWithoutOffset(); // look for actual size of layer on contact Iterator pit = ni.getShape(Poly.newLambdaBuilder()); while (pit.hasNext()) { Poly poly = pit.next(); if (poly.getLayer() == ap.getLayer(0)) { xsize = poly.getBounds2D().getWidth(); ysize = poly.getBounds2D().getHeight(); } } if (arcAngle % 1800 == 0) { width = ysize; } if ((arcAngle - 900) % 1800 == 0) { width = xsize; } } } if (pn.getFunction().isPin()) { for (Iterator it = pi.getConnections(); it.hasNext(); ) { Connection c = it.next(); ArcInst ai = c.getArc(); if (ai.getProto() != ap) continue; double newWidth = c.getArc().getLambdaBaseWidth(); if (width < newWidth) width = newWidth; } } } // check any wires that connect to the export of this portinst in the // prototype, if this is a cell instance if (ni.isCellInstance()) { Cell cell = (Cell)ni.getProto(); Export export = cell.findExport(pi.getPortProto().getName()); PortInst exportedInst = export.getOriginalPort(); double width2 = getArcWidthToUse(exportedInst, ap, arcAngle, ignoreAngle, ep); if (width2 > width) width = width2; } return width; } /** * ContactSize class to determine the arc sizes and contact size between two * objects to be connected by the wirer. This assumes Manhattan wiring. */ protected static class ContactSize { private Rectangle2D contactSize; private int startAngle; private int endAngle; private double startArcWidth; private double endArcWidth; public Rectangle2D getContactSize() { return contactSize; } public int getStartAngle() { return startAngle; } public int getEndAngle() { return endAngle; } public double getStartWidth() { return startArcWidth; } public double getEndWidth() { return endArcWidth; } /** * Determine the contact size, arc sizes, and arc angles based on the * ElectricObjects to be connected, and the start, end, and corner location. * @param startObj the object to route from * @param endObj the object to route to * @param startLoc the start location of the start arc * @param endLoc the end location of the end arc * @param cornerLoc the corner location (end of start arc and start of end arc) * @param startArc start arc type * @param endArc end arc type * @param ignoreAngles whether to ignore angles when determining sizes * @param ep EditingPreferences with default sizes */ public ContactSize(ElectricObject startObj, ElectricObject endObj, Point2D startLoc, Point2D endLoc, Point2D cornerLoc, ArcProto startArc, ArcProto endArc, boolean ignoreAngles, EditingPreferences ep) { AngleAndDimensions start = getAngleAndDimensions(startObj, startLoc, cornerLoc, startArc, endObj == null, ignoreAngles, ep); AngleAndDimensions end = getAngleAndDimensions(endObj, endLoc, cornerLoc, endArc, startObj == null, ignoreAngles, ep); startAngle = start.angle; endAngle = end.angle; EDimension startDim = start.dim; EDimension endDim = end.dim; EDimension startPref = start.pref; EDimension endPref = end.pref; double startW = startDim.getWidth(); double startH = startDim.getHeight(); double endW = endDim.getWidth(); double endH = endDim.getHeight(); if (startW == 0) startW = endW; if (startH == 0) startH = endH; if (endW == 0) endW = startW; if (endH == 0) endH = startH; // put dims in start, prefer arc widths if (endPref.getWidth() > startPref.getWidth()) { startW = endW; } else if (endPref.getWidth() == startPref.getWidth()) { if (endW < startW) startW = endW; } if (endPref.getHeight() > startPref.getHeight()) { startH = endH; } else if (endPref.getHeight() == startPref.getHeight()) { if (endH < startH) startH = endH; } if (endObj == null && ep.isFatWires()) { if (startW > startH) startH = startW; if (startH > startW) startW = startH; } contactSize = new Rectangle2D.Double(cornerLoc.getX()-startW/2.0, cornerLoc.getY()-startH/2.0, startW, startH); if (startAngle % 1800 == 0) startArcWidth = contactSize.getHeight(); else if ((startAngle + 900) % 1800 == 0) startArcWidth = contactSize.getWidth(); else startArcWidth = contactSize.getHeight(); // all non-Manhattan angles if (endAngle % 1800 == 0) endArcWidth = contactSize.getHeight(); else if ((endAngle + 900) % 1800 == 0) endArcWidth = contactSize.getWidth(); else endArcWidth = contactSize.getWidth(); // all non-Manhattan angles } private static class AngleAndDimensions { final int angle; final EDimension dim; final EDimension pref; private AngleAndDimensions(int angle, EDimension dim, EDimension pref) { this.angle = angle; this.dim = dim; this.pref = pref; } } private static AngleAndDimensions getAngleAndDimensions(ElectricObject obj, Point2D loc, Point2D cornerLoc, ArcProto arc, boolean otherObjNull, boolean ignoreAngles, EditingPreferences ep) { double w = 0, h = 0; double prefW = 0, prefH = 0; int angle = 0; if (obj instanceof ArcInst) { ArcInst ai = (ArcInst)obj; angle = ai.getDefinedAngle(); double size = ai.getLambdaBaseWidth(); if (loc.equals(cornerLoc)) { if (angle % 1800 == 0) h = size; else if ((angle + 900) % 1800 == 0) w = size; else w = h = size; } else { angle = GenMath.figureAngle(loc, cornerLoc); if (angle % 1800 == 0) h = size; else if ((angle + 900) % 1800 == 0) w = size; else w = h = size; } if (angle % 1800 == 0) prefH = 1; //pref.setSize(0, 1); if ((angle + 900) % 1800 == 0) prefW = 1; //pref.setSize(1, 0); // special case if (otherObjNull) { w = h = size; prefH = prefW = 1; //pref.setSize(1, 1); } } if (obj instanceof PortInst) { PortInst pi = (PortInst)obj; if (otherObjNull) ignoreAngles = true; if (loc.equals(cornerLoc)) { w = getArcWidthToUse(pi, arc, 900, ignoreAngles, ep); h = getArcWidthToUse(pi, arc, 0, ignoreAngles, ep); } else { angle = GenMath.figureAngle(loc, cornerLoc); if (angle % 1800 == 0) h = getArcWidthToUse(pi, arc, angle, ignoreAngles, ep); else if ((angle + 900) % 1800 == 0) w = getArcWidthToUse(pi, arc, angle, ignoreAngles, ep); else h = w = getArcWidthToUse(pi, arc, angle, true, ep); } } return new AngleAndDimensions(angle, new EDimension(w, h), new EDimension(prefW, prefH)); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy