/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: TiledCell.java
*
* Copyright (c) 2006 Sun Microsystems and Static Free Software
*
* 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.generator.layout.fill;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.DBMath;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.tool.generator.layout.LayoutLib;
import com.sun.electric.tool.generator.layout.TechType;
import com.sun.electric.tool.Job;
import java.awt.geom.Rectangle2D;
import java.util.*;
/**
* User: gg151869
* Date: Sep 19, 2006
*/
public class TiledCell {
private enum Orientation{
VERT_EXTERIOR, // to check if exports on top or bottom must be exported
HORI_EXTERIOR,
INTERIOR
}
private int vddNum, gndNum;
private Cell tileCell;
protected FillGenConfig config; // required by qTree method
private String vddName() {
int n = vddNum++;
return n==0 ? FillCell.VDD_NAME : FillCell.VDD_NAME+"_"+n;
}
private String gndName() {
int n = gndNum++;
return n==0 ? FillCell.GND_NAME : FillCell.GND_NAME+"_"+n;
}
/**
* Constructor used by working with qTree fill.
* @param conf configuration.
*/
public TiledCell(FillGenConfig conf)
{
assert(conf != null);
config = conf;
}
/**
* Method to create the master tiledCell. Note that targetHeight and targetHeight do not necessarily match
* with cellW and cellH if algorithm is used to create a flexible number of tiled cells
@param numX
* @param numY
* @param cell
* @param plans
* @param lib
* @param stdCell
*/
private TiledCell(int numX, int numY, Cell cell, Floorplan[] plans, Library lib)
{
String tiledName = "t"+cell.getName()+"_"+numX+"x"+numY+"{lay}";
tileCell = Cell.newInstance(lib, tiledName);
TechType.TechTypeEnum tech = TechType.TechTypeEnum.getTechTypeEnumFromTechnology(cell.getTechnology());
config = new FillGenConfig(tech);
Rectangle2D bounds = cell.findEssentialBounds();
ERectangle r = cell.getBounds();
Job.error(bounds==null, "missing Essential Bounds");
double cellW = bounds.getWidth();
double cellH = bounds.getHeight();
// put bottom left cell at (0, 0)
double y = 0;
NodeInst[][] rows = newRows(numX, numY);
for (int row=0; row<numY; row++) {
double x = 0;
for (int col=0; col<numX; col++) {
rows[row][col] = LayoutLib.newNodeInst(cell, x, y, G.DEF_SIZE,
G.DEF_SIZE, 0, tileCell);
x += cellW;
}
y += cellH;
}
connectAllPortInsts(config.getTechType(), tileCell);
exportUnconnectedPortInsts(rows, plans[plans.length-1].horizontal, tileCell);
// addEssentialBounds(cellW, cellH, numX, numY, tileCell);
addEssentialBounds1(r.getX(), r.getY(), cellW, cellH, numX, numY, tileCell);
}
private static class OrderPortInstsByName implements Comparator<PortInst> {
private String base(String s) {
int under = s.indexOf("_");
if (under==-1) return s;
return s.substring(0, under);
}
private int subscript(String s) {
int under = s.indexOf("_");
if (under==-1) return 0;
String num = s.substring(under+1, s.length());
return Integer.parseInt(num);
}
public int compare(PortInst p1, PortInst p2) {
String n1 = p1.getPortProto().getName();
String n2 = p2.getPortProto().getName();
String base1 = base(n1);
String base2 = base(n2);
if (!base1.equals(base2)) {
return n1.compareTo(n2);
} else {
int sub1 = subscript(n1);
int sub2 = subscript(n2);
return sub1-sub2;
}
}
}
public static ArrayList<PortInst> connectAllPortInsts(TechType tech, Cell cell) {
// get all the ports
ArrayList<PortInst> ports = new ArrayList<PortInst>();
for (Iterator<NodeInst> it=cell.getNodes(); it.hasNext();) {
NodeInst ni = it.next();
for (Iterator<PortInst> pIt=ni.getPortInsts(); pIt.hasNext();) {
PortInst pi = pIt.next();
ports.add(pi);
}
}
Collections.sort(ports, new OrderPortInstsByName());
FillRouter.connectCoincident(tech, ports);
return ports;
}
private static Orientation orientation(Rectangle2D bounds, PortInst pi) {
EPoint center = pi.getCenter();
double portX = center.getX();
double portY = center.getY();
double minX = bounds.getMinX();
double maxX = bounds.getMaxX();
double minY = bounds.getMinY();
double maxY = bounds.getMaxY();
if (DBMath.areEquals(portX,minX) || DBMath.areEquals(portX,maxX)) return Orientation.VERT_EXTERIOR;
if (DBMath.areEquals(portY,minY) || DBMath.areEquals(portY,maxY)) return Orientation.HORI_EXTERIOR;
return Orientation.INTERIOR;
}
/** return a list of all PortInsts of ni that aren't connected to
* something. */
private static ArrayList<PortInst> getUnconnectedPortInsts(List<Orientation> orientationList, NodeInst ni) {
Rectangle2D bounds = ni.findEssentialBounds();
if (bounds == null)
bounds = ni.getBounds();
ArrayList<PortInst> ports = new ArrayList<PortInst>();
for (Iterator<PortInst> it=ni.getPortInsts(); it.hasNext();) {
PortInst pi = it.next();
if (!pi.hasConnections())
// Iterator conns = pi.getConnections();
// if (!conns.hasNext())
{
Orientation or = orientation(bounds,pi);
if (orientationList.contains(or))
ports.add(pi);
}
}
return ports;
}
private void exportPortInsts(List<PortInst> ports, Cell tiled) {
Collections.sort(ports, new OrderPortInstsByName());
for (PortInst pi : ports) {
PortProto pp = pi.getPortProto();
PortCharacteristic role = pp.getCharacteristic();
if (role==FillCell.VDD_CHARACTERISTIC) {
//System.out.println(pp.getName());
Export e = Export.newInstance(tiled, pi, vddName());
e.setCharacteristic(role);
} else if (role==FillCell.GND_CHARACTERISTIC) {
//System.out.println(pp.getName());
Export e = Export.newInstance(tiled, pi, gndName());
e.setCharacteristic(role);
} else {
Job.error(true, "unrecognized Characteristic");
}
}
}
/** export all PortInsts of all NodeInsts in insts that aren't connected
* to something */
private static final Orientation[] horizontalPlan = {Orientation.VERT_EXTERIOR, Orientation.HORI_EXTERIOR, Orientation.INTERIOR};
private static final Orientation[] verticalPlan = {Orientation.HORI_EXTERIOR, Orientation.VERT_EXTERIOR, Orientation.INTERIOR};
public void exportUnconnectedPortInsts(NodeInst[][] rows,
boolean isPlanHorizontal, Cell tiled) {
// Subtle! If top layer is horizontal then begin numbering exports on
// vertical edges of boundary first. This ensures that fill6_2x2 and
// fill56_2x2 have matching port names on the vertical edges.
// Always number interior exports last so they never interfere with
// perimeter exports.
Orientation[] orientations = (isPlanHorizontal) ? horizontalPlan : verticalPlan;
List<Orientation> list = new ArrayList<Orientation>();
for (int o=0; o<3; o++)
{
int numRows = rows.length;
list.clear();
list.add(orientations[o]);
Orientation orientation = orientations[o];
for (int row=0; row<numRows; row++)
{
int numCols = rows[row].length;
for (int col=0; col<numCols; col++)
{
// boolean forceHExport = (numRows > 1 && rows[(row+1)%2][col].getName().startsWith("empty"));
// boolean forceVExport = (numCols > 1 && rows[row][(col+1)%2].getName().startsWith("empty"));
// boolean forceExport = (orientation == Orientation.HORI_EXTERIOR) ? forceHExport : forceVExport;
// if (forceExport)
// {
// list.add(Orientation.INTERIOR);
// }
if (orientation!=Orientation.INTERIOR || row==col) {
List<PortInst> ports = getUnconnectedPortInsts(list, rows[row][col]);
exportPortInsts(ports, tiled);
}
}
}
}
}
private NodeInst[][] newRows(int numX, int numY) {
NodeInst[][] rows = new NodeInst[numY][];
for (int row=0; row<numY; row++) {
rows[row] = new NodeInst[numX];
}
return rows;
}
/** Geometric center of bottom left cell is at (0, 0). */
// private void addEssentialBounds(double cellW, double cellH,
// int numX, int numY, Cell tiled) {
// double blX = -cellW/2;
// double blY = -cellH/2;
// double tlX = cellW/2 + (numX-1)*cellW;
// double tlY = cellH/2 + (numY-1)*cellH;
// LayoutLib.newNodeInst(Tech.essentialBounds, blX, blY,
// G.DEF_SIZE, G.DEF_SIZE, 180, tiled);
// LayoutLib.newNodeInst(Tech.essentialBounds, tlX, tlY,
// G.DEF_SIZE, G.DEF_SIZE, 0, tiled);
// }
private void addEssentialBounds1(double x, double y, double cellW, double cellH, int numX, int numY, Cell tiled)
{
double x2 = x + numX*cellW;
double y2 = y + numY*cellH;
TechType tech = config.getTechType();
LayoutLib.newNodeInst(tech.essentialBounds(), x, y,
G.DEF_SIZE, G.DEF_SIZE, 180, tiled);
LayoutLib.newNodeInst(tech.essentialBounds(), x2, y2,
G.DEF_SIZE, G.DEF_SIZE, 0, tiled);
}
public static Cell makeTiledCell(int numX, int numY, Cell cell, Floorplan[] plans, Library lib) {
TiledCell tile = new TiledCell(numX, numY, cell, plans, lib);
return tile.tileCell;
}
}