/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: FPGA.java
* FPGA, a customizable technology.
* Written by Steven M. Rubin
*
* Copyright (c) 2005 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.technology.technologies;
import com.sun.electric.database.ImmutableArcInst;
import com.sun.electric.database.geometry.EGraphics;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.GenMath;
import com.sun.electric.database.geometry.Orientation;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.Nodable;
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.prototype.PortCharacteristic;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.TextUtils;
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.topology.RTBounds;
import com.sun.electric.database.variable.EditWindow0;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.UserInterface;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.AbstractShapeBuilder;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.EdgeH;
import com.sun.electric.technology.EdgeV;
import com.sun.electric.technology.Foundry;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.TechFactory;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.io.FileType;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.dialogs.OpenFile;
import com.sun.electric.tool.user.dialogs.PromptAt;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* This is the FPGA Technology.
*/
public class FPGA extends Technology
{
/** the FPGA Technology object. */ public static FPGA tech() { return (FPGA)findTechnology("fpga"); }
private final Layer wireLayer, componentLayer, pipLayer, repeaterLayer;
private final ArcProto wireArc;
private final PrimitiveNode wirePinNode, pipNode, repeaterNode;
public FPGA(Generic generic, TechFactory techFactory)
{
super(generic, techFactory, Foundry.Type.NONE, 1);
setTechShortName("FPGA");
setTechDesc("FPGA Building-Blocks");
setFactoryScale(2000, true); // in nanometers: really 2 microns
setStaticTechnology();
setNonStandard();
setNoPrimitiveNodes();
//**************************************** LAYERS ****************************************
/** Wire layer */
wireLayer = Layer.newInstance(this, "Wire",
new EGraphics(false, false, null, 0, 255,0,0,1,true,
new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
/** Component layer */
componentLayer = Layer.newInstance(this, "Component",
new EGraphics(false, false, null, 0, 0,0,0,1,true,
new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
/** Pip layer */
pipLayer = Layer.newInstance(this, "Pip",
new EGraphics(false, false, null, 0, 0,255,0,1,true,
new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
/** Repeater layer */
repeaterLayer = Layer.newInstance(this, "Repeater",
new EGraphics(false, false, null, 0, 0,0,255,1,true,
new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
// The layer functions
wireLayer.setFunction(Layer.Function.METAL1); // wire
componentLayer.setFunction(Layer.Function.ART); // component
pipLayer.setFunction(Layer.Function.ART); // pip
repeaterLayer.setFunction(Layer.Function.ART); // repeater
//**************************************** ARC ****************************************
/** wire arc */
wireArc = newArcProto("wire", 0, 0.0, ArcProto.Function.METAL1,
new Technology.ArcLayer(wireLayer, 0, Poly.Type.FILLED)
);
wireArc.setFactoryFixedAngle(true);
wireArc.setFactorySlidable(false);
wireArc.setFactoryAngleIncrement(45);
//**************************************** NODES ****************************************
/** wire pin */
wirePinNode = PrimitiveNode.newInstance("Wire_Pin", this, 1, 1, null,
new Technology.NodeLayer []
{
new Technology.NodeLayer(wireLayer, 0, Poly.Type.DISC, Technology.NodeLayer.POINTS, new Technology.TechPoint [] {
new Technology.TechPoint(EdgeH.makeCenter(), EdgeV.makeCenter()),
new Technology.TechPoint(EdgeH.makeRightEdge(), EdgeV.makeCenter())})
});
wirePinNode.addPrimitivePortsFixed(new PrimitivePort []
{
PrimitivePort.newInstance(this, wirePinNode, new ArcProto[] {wireArc}, "wire", 0,180, 0, PortCharacteristic.UNKNOWN,
EdgeH.makeCenter(), EdgeV.makeCenter(), EdgeH.makeCenter(), EdgeV.makeCenter())
});
wirePinNode.setFunction(PrimitiveNode.Function.PIN);
wirePinNode.setSquare();
wirePinNode.setWipeOn1or2();
/** pip */
pipNode = PrimitiveNode.newInstance("Pip", this, 2, 2, null,
new Technology.NodeLayer []
{
new Technology.NodeLayer(pipLayer, 0, Poly.Type.FILLED, Technology.NodeLayer.BOX, new Technology.TechPoint [] {
new Technology.TechPoint(EdgeH.makeLeftEdge(), EdgeV.makeBottomEdge()),
new Technology.TechPoint(EdgeH.makeRightEdge(), EdgeV.makeTopEdge())})
});
pipNode.addPrimitivePortsFixed(new PrimitivePort []
{
PrimitivePort.newInstance(this, pipNode, new ArcProto[] {wireArc}, "pip", 0,180, 0, PortCharacteristic.UNKNOWN,
EdgeH.makeCenter(), EdgeV.makeCenter(), EdgeH.makeCenter(), EdgeV.makeCenter())
});
pipNode.setFunction(PrimitiveNode.Function.CONNECT);
pipNode.setSquare();
/** repeater */
repeaterNode = PrimitiveNode.newInstance("Repeater", this, 10, 3, null,
new Technology.NodeLayer []
{
new Technology.NodeLayer(repeaterLayer, 0, Poly.Type.FILLED, Technology.NodeLayer.BOX, new Technology.TechPoint [] {
new Technology.TechPoint(EdgeH.makeLeftEdge(), EdgeV.makeBottomEdge()),
new Technology.TechPoint(EdgeH.makeRightEdge(), EdgeV.makeTopEdge())})
});
repeaterNode.addPrimitivePortsFixed(new PrimitivePort []
{
PrimitivePort.newInstance(this, repeaterNode, new ArcProto[] {wireArc}, "a", 180,45, 0, PortCharacteristic.UNKNOWN,
EdgeH.makeLeftEdge(), EdgeV.makeCenter(), EdgeH.makeLeftEdge(), EdgeV.makeCenter()),
PrimitivePort.newInstance(this, repeaterNode, new ArcProto[] {wireArc}, "b", 0,45, 1, PortCharacteristic.UNKNOWN,
EdgeH.makeRightEdge(), EdgeV.makeCenter(), EdgeH.makeRightEdge(), EdgeV.makeCenter())
});
repeaterNode.setFunction(PrimitiveNode.Function.CONNECT);
// Building information for palette
loadFactoryMenuPalette(FPGA.class.getResource("fpgaMenu.xml"));
// Foundry
newFoundry(Foundry.Type.NONE, null);
}
/******************** TREE STRUCTURE FOR ARCHITECTURE FILE ********************/
/** max depth of FPGA nesting */ private static final int MAXDEPTH = 50;
private static class LispTree
{
private String keyword;
private int lineNumber;
private List<Object> values;
LispTree()
{
values = new ArrayList<Object>();
}
void add(Object obj) { values.add(obj); }
int size() { return values.size(); }
boolean isLeaf(int i) { return !(values.get(i) instanceof LispTree); }
boolean isBranch(int i) { return values.get(i) instanceof LispTree; }
String getLeaf(int i) { return (String)values.get(i); }
LispTree getBranch(int i) { return (LispTree)values.get(i); }
};
private static LispTree [] treeStack = new LispTree[MAXDEPTH];
private static int treeDepth;
private static LispTree treePosition;
/******************** ADDITIONAL INFORMATION ABOUT PRIMITIVES ********************/
/** level of display */ private static final int DISPLAYLEVEL = 07;
/** display no internals */ private static final int NOPRIMDISPLAY = 0;
/** display all internals */ private static final int FULLPRIMDISPLAY = 01;
/** display only active internals */ private static final int ACTIVEPRIMDISPLAY = 02;
/** set to display text */ private static final int TEXTDISPLAY = 010;
/** set if segment or pip is active */ private static final int ACTIVEPART = 1;
/** saved area for segment/pip activity */ private static final int ACTIVESAVE = 2;
private static class FPGAPort
{
String name;
double posX, posY;
int con;
PortCharacteristic characteristic;
PrimitivePort pp;
};
private static class FPGANet
{
String name;
int segActive;
Point2D [] segFrom;
Point2D [] segTo;
};
private static class FPGAPip
{
String name;
int pipActive;
int con1, con2;
double posX, posY;
};
private static class FPGANode extends PrimitiveNode
{
FPGAPort [] portList;
FPGANet [] netList;
FPGAPip [] pipList;
protected FPGANode(String protoName, Technology tech, double defWidth, double defHeight,
Technology.NodeLayer [] layers)
{
super(protoName, tech, EPoint.ORIGIN, EPoint.ORIGIN, null, defWidth, defHeight,
ERectangle.ORIGIN, ERectangle.ORIGIN, layers);
}
int numPorts()
{
if (portList == null) return 0;
return portList.length;
}
int numNets()
{
if (netList == null) return 0;
return netList.length;
}
int numPips()
{
if (pipList == null) return 0;
return pipList.length;
}
};
/** key of Variable holding active pips. */ private static final Variable.Key ACTIVEPIPS_KEY = Variable.newKey("FPGA_activepips");
/** key of Variable holding active repeaters. */ private static final Variable.Key ACTIVEREPEATERS_KEY = Variable.newKey("FPGA_activerepeaters");
// /** key of Variable holding cache of pips on node. */ private static final Variable.Key NODEPIPCACHE_KEY = Variable.newKey("FPGA_nodepipcache");
// /** key of Variable holding cache of active arcs. */ private static final Variable.Key ARCACTIVECACHE_KEY = Variable.newKey("FPGA_arcactivecache");
/** name of current repeater for activity examining */ private String repeaterName;
/** nonzero if current repeater is found to be active */private boolean repeaterActive;
/** what is being displayed */ private int internalDisplay = FULLPRIMDISPLAY | TEXTDISPLAY;
/** whether the technology has been read */ private boolean defined = false;
private static final Technology.NodeLayer[] NULLNODELAYER = new Technology.NodeLayer[0];
// /**
// * Method to return a list of Polys that describe a given NodeInst.
// * This method overrides the general one in the Technology object
// * because of the unusual primitives in this Technology.
// * @param ni the NodeInst to describe.
// * @param electrical true to get the "electrical" layers.
// * This makes no sense for Schematics primitives.
// * @param reasonable true to get only a minimal set of contact cuts in large contacts.
// * This makes no sense for Schematics primitives.
// * @param primLayers an array of NodeLayer objects to convert to Poly objects.
// * @return an array of Poly objects.
// */
// @Override
// protected Poly [] getShapeOfNode(NodeInst ni, boolean electrical, boolean reasonable, Technology.NodeLayer [] primLayers) {
// return getShapeOfNode(ni, null, null, electrical, reasonable, primLayers);
// }
//
// /**
// * Method to return a list of Polys that describe a given NodeInst.
// * This method overrides the general one in the Technology object
// * because of the unusual primitives in this Technology.
// * @param ni the NodeInst to describe.
// * @param wnd the window in which this node will be drawn.
// * @param context the VarContext to this node in the hierarchy.
// * @param electrical true to get the "electrical" layers.
// * This makes no sense for Schematics primitives.
// * @param reasonable true to get only a minimal set of contact cuts in large contacts.
// * This makes no sense for Schematics primitives.
// * @param primLayers an array of NodeLayer objects to convert to Poly objects.
// * @return an array of Poly objects.
// */
// private Poly [] getShapeOfNode(NodeInst ni, EditWindow0 wnd, VarContext context, boolean electrical, boolean reasonable, Technology.NodeLayer [] primLayers)
// {
// if (ni.isCellInstance()) return null;
//
// PrimitiveNode np = (PrimitiveNode)ni.getProto();
// if (np == wirePinNode)
// {
// if (ni.pinUseCount()) primLayers = NULLNODELAYER;
// } else if (np == repeaterNode)
// {
// if ((internalDisplay&DISPLAYLEVEL) == ACTIVEPRIMDISPLAY)
// {
// if (!repeaterActive(ni)) primLayers = NULLNODELAYER;
// }
// } else if (np instanceof FPGANode)
// {
// // dynamic primitive
// FPGANode fn = (FPGANode)np;
//
// // hard reset of all segment and pip activity
// int numPips = 0, numSegs = 0;
// for(int i=0; i<fn.numNets(); i++) fn.netList[i].segActive = 0;
// for(int i=0; i<fn.numPips(); i++) fn.pipList[i].pipActive = 0;
//
// switch (internalDisplay & DISPLAYLEVEL)
// {
// case NOPRIMDISPLAY:
// break;
// case ACTIVEPRIMDISPLAY:
// // count number of active nets and pips
//
// // determine the active segments and pips
// reEvaluatePips(ni, fn, context);
//
// // save the activity bits
// for(int i=0; i<fn.numNets(); i++)
// if ((fn.netList[i].segActive&ACTIVEPART) != 0)
// fn.netList[i].segActive |= ACTIVESAVE;
// for(int i=0; i<fn.numPips(); i++)
// if ((fn.pipList[i].pipActive&ACTIVEPART) != 0)
// fn.pipList[i].pipActive |= ACTIVESAVE;
//
// // propagate inactive segments to others that may be active
// if (context != null && context.getNodable() != null)
// {
// VarContext higher = context.pop();
// for(int i=0; i<fn.numNets(); i++)
// {
// if ((fn.netList[i].segActive&ACTIVESAVE) != 0) continue;
// boolean found = false;
// for(int j=0; j<fn.numPorts(); j++)
// {
// if (fn.portList[j].con != i) continue;
// for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
// {
// Connection con = it.next();
// if (con.getPortInst().getPortProto() != fn.portList[j].pp) continue;
// ArcInst ai = con.getArc();
// int otherEnd = 1 - con.getEndIndex();
// if (arcEndActive(ai, otherEnd, higher)) { found = true; break; }
// }
// if (found) break;
// }
// if (found) fn.netList[i].segActive |= ACTIVESAVE;
// }
// }
//
// // add up the active segments
// for(int i=0; i<fn.numPips(); i++)
// if ((fn.pipList[i].pipActive&ACTIVESAVE) != 0) numPips++;
// for(int i=0; i<fn.numNets(); i++)
// if ((fn.netList[i].segActive&ACTIVESAVE) != 0)
// numSegs += fn.netList[i].segFrom.length;
// break;
// case FULLPRIMDISPLAY:
// for(int i=0; i<fn.numNets(); i++)
// {
// fn.netList[i].segActive |= ACTIVESAVE;
// numSegs += fn.netList[i].segFrom.length;
// }
// break;
// }
// int total = 1 + numPips + numSegs;
// if ((internalDisplay&TEXTDISPLAY) != 0)
// {
// total++;
// if (wnd != null) total += ni.numDisplayableVariables(true);
// }
//
// // construct the polygon array
// Poly [] polys = new Poly[total];
//
// // add the basic box layer
// double xCenter = ni.getTrueCenterX();
// double yCenter = ni.getTrueCenterY();
// double xSize = ni.getXSize();
// double ySize = ni.getYSize();
// Point2D [] pointList = Poly.makePoints(xCenter - xSize/2, xCenter + xSize/2, yCenter - ySize/2, yCenter + ySize/2);
// polys[0] = new Poly(pointList);
// polys[0].setStyle(fn.getNodeLayers()[0].getStyle());
// polys[0].setLayer(componentLayer);
// int fillPos = 1;
//
// // add in the pips
// for(int i=0; i<fn.numPips(); i++)
// {
// if ((fn.pipList[i].pipActive&ACTIVESAVE) == 0) continue;
// double x = xCenter + fn.pipList[i].posX;
// double y = yCenter + fn.pipList[i].posY;
// polys[fillPos] = new Poly(Poly.makePoints(x-1, x+1, y-1, y+1));
// polys[fillPos].setStyle(Poly.Type.FILLED);
// polys[fillPos].setLayer(pipLayer);
// fillPos++;
// }
//
// // add in the network segments
// for(int i=0; i<fn.numNets(); i++)
// {
// if ((fn.netList[i].segActive&ACTIVESAVE) == 0) continue;
// for(int j=0; j<fn.netList[i].segFrom.length; j++)
// {
// double fX = xCenter + fn.netList[i].segFrom[j].getX();
// double fY = yCenter + fn.netList[i].segFrom[j].getY();
// double tX = xCenter + fn.netList[i].segTo[j].getX();
// double tY = yCenter + fn.netList[i].segTo[j].getY();
// Point2D [] line = new Point2D[2];
// line[0] = new Point2D.Double(fX, fY);
// line[1] = new Point2D.Double(tX, tY);
// polys[fillPos] = new Poly(line);
// polys[fillPos].setStyle(Poly.Type.OPENED);
// polys[fillPos].setLayer(wireLayer);
// fillPos++;
// }
// }
//
// // add the primitive name if requested
// if ((internalDisplay&TEXTDISPLAY) != 0)
// {
// polys[fillPos] = new Poly(pointList);
// polys[fillPos].setStyle(Poly.Type.TEXTBOX);
// polys[fillPos].setLayer(componentLayer);
// polys[fillPos].setString(fn.getName());
// TextDescriptor td = TextDescriptor.EMPTY.withRelSize(3);
// polys[fillPos].setTextDescriptor(td);
// fillPos++;
//
// // add in displayable variables
// if (wnd != null)
// {
// Rectangle2D rect = ni.getUntransformedBounds();
// ni.addDisplayableVariables(rect, polys, fillPos, wnd, true);
// }
// }
// return polys;
// }
//
// return super.getShapeOfNode(ni, electrical, reasonable, primLayers);
// }
//
// /**
// * Fill the polygons that describe arc "a".
// * @param b AbstractShapeBuilder to fill polygons.
// * @param a the ImmutableArcInst that is being described.
// */
// @Override
// protected void getShapeOfArc(AbstractShapeBuilder b, ImmutableArcInst a) {
// super.getShapeOfArc(b, a);
// }
/**
* Tells if arc can be drawn by simplified algorithm
* FPGA arcs are not easy
* @param a arc to test
* @param explain if true then print explanation why arc is not easy
* @return false
*/
@Override
public boolean isEasyShape(ImmutableArcInst a, boolean explain) {
return false;
}
/******************** TECHNOLOGY INTERFACE SUPPORT ********************/
private boolean arcEndActive(ArcInst ai, int j, VarContext curContext)
{
// examine end
PortInst pi = ai.getPortInst(j);
NodeInst ni = pi.getNodeInst();
PortProto pp = pi.getPortProto();
NodeProto np = ni.getProto();
if (ni.isCellInstance())
{
// follow down into cell
VarContext down = curContext.push(ni);
NodeInst subni = ((Export)pp).getOriginalPort().getNodeInst();
for(Iterator<Connection> it = subni.getConnections(); it.hasNext(); )
{
Connection nextCon = it.next();
ArcInst oAi = nextCon.getArc();
int newEnd = 0;
if (oAi.getPortInst(0).getNodeInst() == subni) newEnd = 1;
if (arcEndActive(oAi, newEnd, down)) return true;
}
return false;
}
// primitive: see if it is one of ours
if (np instanceof FPGANode)
{
FPGANode fn = (FPGANode)np;
reEvaluatePips(ni, fn, curContext);
for(int i = 0; i < fn.numPorts(); i++)
{
if (fn.portList[i].pp != pp) continue;
int index = fn.portList[i].con;
if (index >= 0 && fn.netList != null)
{
if ((fn.netList[index].segActive&ACTIVEPART) != 0) return true;
}
break;
}
}
// propagate
Cell parent = ai.getParent();
if (parent != null)
{
Netlist nl = parent.getNetlist();
Network net = nl.getNetwork(ni, pp, 0);
if (net != null)
{
for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
{
Connection nextCon = it.next();
ArcInst oAi = nextCon.getArc();
if (oAi == ai) continue;
Network oNet = nl.getNetwork(oAi, 0);
if (oNet != net) continue;
int newEnd = 1 - nextCon.getEndIndex();
if (arcEndActive(oAi, newEnd, curContext)) return true;
}
VarContext higher = curContext.pop();
if (higher != null && higher.getNodable() != null)
{
NodeInst oNi = (NodeInst)higher.getNodable();
for (Iterator<Export> it = ni.getExports(); it.hasNext(); )
{
Export opp = it.next();
Network oNet = nl.getNetwork(opp, 0);
if (oNet != net) continue;
for(Iterator<Connection> uIt = oNi.getConnections(); uIt.hasNext(); )
{
Connection nextCon = uIt.next();
ArcInst oAi = nextCon.getArc();
if (nextCon.getPortInst().getPortProto() != opp) continue;
int newEnd = 1 - nextCon.getEndIndex();
if (arcEndActive(oAi, newEnd, higher)) return true;
}
}
}
}
}
return false;
}
/**
* Method to reevaluate primitive node "ni" (which is associated with internal
* structure "fn"). Finds programming of pips and sets pip and net activity.
*/
private void reEvaluatePips(NodeInst ni, FPGANode fn, VarContext context)
{
// primitives with no pips or nets need no evaluation
if (fn.numNets() == 0 && fn.numPips() == 0) return;
// reevaluate: presume all nets and pips are inactive
for(int i=0; i<fn.numNets(); i++) fn.netList[i].segActive &= ~ACTIVEPART;
for(int i=0; i<fn.numPips(); i++) fn.pipList[i].pipActive &= ~ACTIVEPART;
// look for pip programming
findVariableObjects(fn, ni, ACTIVEPIPS_KEY, true, context);
// set nets active where they touch active pips
for(int i=0; i<fn.numPips(); i++)
{
FPGAPip fPip = fn.pipList[i];
if ((fPip.pipActive&ACTIVEPART) == 0) continue;
if (fPip.con1 > 0) fn.netList[fPip.con1].segActive |= ACTIVEPART;
if (fPip.con2 > 0) fn.netList[fPip.con2].segActive |= ACTIVEPART;
}
}
/**
* Method to examine primitive node "ni" and return true if the repeater is active.
*/
private boolean repeaterActive(NodeInst ni)
{
repeaterName = ni.getName();
repeaterActive = false;
findVariableObjects(null, ni, ACTIVEREPEATERS_KEY, false, null);
return repeaterActive;
}
Nodable [] path = new Nodable[100];
private void findVariableObjects(FPGANode fn, NodeInst ni, Variable.Key varKey, boolean setPips, VarContext context)
{
// search hierarchical path
int depth = 0;
path[depth++] = ni;
while (context != null)
{
Nodable niClimb = context.getNodable();
if (niClimb == null) break;
path[depth++] = niClimb;
context = context.pop();
}
// look for programming variables on the nodes
for(int c=0; c<depth; c++)
{
Nodable niClimb = path[c];
Variable var = niClimb.getVar(varKey);
if (var == null) continue;
// found pip settings: evaluate them
String pt = (String)var.getObject();
String [] pipNames = pt.split(" ");
for(int i=0; i<pipNames.length; i++)
{
String start = pipNames[i];
if (start.length() == 0) continue;
// find pip name in "start"
String [] pipParts = start.split("\\.");
if (pipParts.length == 0 || pipParts.length > depth) continue;
boolean pathGood = true;
for(int j=0; j<pipParts.length-1; j++)
{
if (!pipParts[j].equalsIgnoreCase(path[depth-2-j].getName()))
{
pathGood = false;
break;
}
}
if (pathGood)
{
String lastPart = pipParts[pipParts.length-1];
if (setPips)
{
for(int k=0; k<fn.numPips(); k++)
if (fn.pipList[k].name.equalsIgnoreCase(lastPart))
{
fn.pipList[k].pipActive |= ACTIVEPART;
}
} else
{
if (repeaterName.equalsIgnoreCase(lastPart)) repeaterActive = true;
}
}
}
break;
}
}
/******************** TECHNOLOGY CONTROL ********************/
/**
* Method to read an architecture file and customize the FPGA technology.
* Prompts for a file and reads it.
* @param placeAndWire true to build the primitives and structures; false to simply build the primitives.
*/
public void readArchitectureFile(boolean placeAndWire)
{
if (defined)
{
System.out.println("This technology already has primitives defined");
return;
}
// get architecture file
String fileName = OpenFile.chooseInputFile(FileType.FPGA, null);
if (fileName == null) return;
// turn the tree into primitives
new BuildTechnology(this, fileName, placeAndWire);
}
/**
* Method to set the wire display level.
* @param level 0 to show no wires; 1 to show active wires; 2 to show all wires.
*/
public void setWireDisplay(int level)
{
switch (level)
{
case 0: // no wires
internalDisplay = (internalDisplay & ~DISPLAYLEVEL) | NOPRIMDISPLAY;
break;
case 1: // active wires
internalDisplay = (internalDisplay & ~DISPLAYLEVEL) | ACTIVEPRIMDISPLAY;
break;
case 2: // all wires
internalDisplay = (internalDisplay & ~DISPLAYLEVEL) | FULLPRIMDISPLAY;
break;
}
UserInterface ui = Job.getUserInterface();
ui.repaintAllWindows();
}
/**
* Method to set the text display level.
* @param show true to see text, false to hide text.
*/
public void setTextDisplay(boolean show)
{
if (show) internalDisplay |= TEXTDISPLAY; else
internalDisplay &= ~TEXTDISPLAY;
UserInterface ui = Job.getUserInterface();
ui.repaintAllWindows();
}
/**
* Method to program the currently selected PIPs.
*/
public void programPips()
{
UserInterface ui = Job.getUserInterface();
EditWindow_ wnd = ui.getCurrentEditWindow_();
if (wnd == null) return;
ElectricObject eObj = wnd.getOneElectricObject(NodeInst.class);
if (eObj == null) return;
NodeInst ni = (NodeInst)eObj;
String pips = "";
Variable var = ni.getVar(ACTIVEPIPS_KEY);
if (var != null) pips = (String)var.getObject();
String newPips = PromptAt.showPromptAt(wnd, ni, "Edit Pips",
"Pips on this node:", pips);
if (newPips == null) return;
new SetPips(ni, newPips);
}
/**
* This class sets pip programming on a node.
*/
private static class SetPips extends Job
{
private NodeInst ni;
private String newPips;
private SetPips(NodeInst ni, String newPips)
{
super("Program Pips", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.ni = ni;
this.newPips = newPips;
startJob();
}
public boolean doIt() throws JobException
{
ni.newVar(ACTIVEPIPS_KEY, newPips);
return true;
}
public void terminateOK()
{
UserInterface ui = Job.getUserInterface();
ui.repaintAllWindows();
}
}
/**
* This class implement the command to build an FPGA technology.
*/
private static class BuildTechnology extends Job
{
private FPGA tech;
private String fileName;
private boolean placeAndWire;
private Cell topCell;
private BuildTechnology(FPGA tech, String fileName, boolean placeAndWire)
{
super("Build FPGA Technology", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.tech = tech;
this.fileName = fileName;
this.placeAndWire = placeAndWire;
startJob();
}
public boolean doIt() throws JobException
{
// read the file
LispTree lt = tech.readFile(fileName);
if (lt == null)
throw new JobException("Error reading file");
int total = tech.makePrimitives(lt);
System.out.println("Created " + total + " primitives");
// place and wire the primitives
if (placeAndWire)
{
topCell = tech.placePrimitives(lt);
fieldVariableChanged("topCell");
}
return true;
}
public void terminateOK()
{
if (topCell != null)
{
// display top cell
UserInterface ui = Job.getUserInterface();
ui.displayCell(topCell);
}
}
}
/**
* Method to read the FPGA file in "f" and create a LISPTREE structure which is returned.
* Returns zero on error.
*/
private LispTree readFile(String fileName)
{
// make the tree top
LispTree treeTop = new LispTree();
treeTop.keyword = "TOP";
// initialize current position and stack
treePosition = treeTop;
treeDepth = 0;
URL url = TextUtils.makeURLToFile(fileName);
try
{
URLConnection urlCon = url.openConnection();
InputStreamReader is = new InputStreamReader(urlCon.getInputStream());
LineNumberReader lnr = new LineNumberReader(is);
// read the file
for(;;)
{
// get the next line of text
String line = lnr.readLine();
if (line == null) break;
// stop now if it is a comment
line = line.trim();
if (line.length() == 0) continue;
if (line.charAt(0) == '#') continue;
// keep parsing it
int pt = 0;
for(;;)
{
// skip spaces
while (pt < line.length() && Character.isWhitespace(line.charAt(pt))) pt++;
if (pt >= line.length()) break;
// check for special characters
char chr = line.charAt(pt);
if (chr == ')')
{
if (pushKeyword(line.substring(pt, pt+1), lnr)) return null;
pt++;
continue;
}
// gather a keyword
int ptEnd = pt;
for(;;)
{
if (ptEnd >= line.length()) break;
char chEnd = line.charAt(ptEnd);
if (chEnd == ')' || Character.isWhitespace(chEnd)) break;
if (chEnd == '"')
{
ptEnd++;
for(;;)
{
if (ptEnd >= line.length() || line.charAt(ptEnd) == '"') break;
ptEnd++;
}
if (ptEnd < line.length()) ptEnd++;
break;
}
ptEnd++;
}
if (pushKeyword(line.substring(pt, ptEnd), lnr)) return null;
pt = ptEnd;
}
}
lnr.close();
System.out.println(fileName + " read");
} catch (IOException e)
{
System.out.println("Error reading " + fileName);
return null;
}
if (treeDepth != 0)
{
System.out.println("Not enough close parenthesis in file");
return null;
}
return treeTop;
}
/**
* Method to add the next keyword "keyword" to the lisp tree in the globals.
* Returns true on error.
*/
private boolean pushKeyword(String keyword, LineNumberReader lnr)
{
if (keyword.startsWith("("))
{
if (treeDepth >= MAXDEPTH)
{
System.out.println("Nesting too deep (more than " + MAXDEPTH + ")");
return true;
}
// create a new tree branch
LispTree newTree = new LispTree();
newTree.lineNumber = lnr.getLineNumber();
// add branch to previous branch
treePosition.add(newTree);
// add keyword
int pt = 1;
while (pt < keyword.length() && Character.isWhitespace(keyword.charAt(pt))) pt++;
newTree.keyword = keyword.substring(pt);
// push tree onto stack
treeStack[treeDepth] = treePosition;
treeDepth++;
treePosition = newTree;
return false;
}
if (keyword.equals(")"))
{
// pop tree stack
if (treeDepth <= 0)
{
System.out.println("Too many close parenthesis");
return true;
}
treeDepth--;
treePosition = treeStack[treeDepth];
return false;
}
// just add the atomic keyword
if (keyword.startsWith("\"") && keyword.endsWith("\""))
keyword = keyword.substring(1, keyword.length()-1);
treePosition.add(keyword);
return false;
}
/******************** ARCHITECTURE PARSING: PRIMITIVES ********************/
/**
* Method to parse the entire tree and create primitives.
* Returns the number of primitives made.
*/
private int makePrimitives(LispTree lt)
{
// look through top level for the "primdef"s
int total = 0;
for(int i=0; i<lt.size(); i++)
{
if (lt.isLeaf(i)) continue;
LispTree subLT = lt.getBranch(i);
if (!subLT.keyword.equalsIgnoreCase("primdef")) continue;
// create the primitive
if (makePrimitive(subLT)) return(0);
total++;
}
return total;
}
/**
* Method to create a primitive from a subtree "lt".
* Tree has "(primdef...)" structure.
*/
private boolean makePrimitive(LispTree lt)
{
// find all of the pieces of this primitive
LispTree ltAttribute = null, ltNets = null, ltPorts = null, ltComponents = null;
String primName = null;
String primSizeX = null;
String primSizeY = null;
for(int i=0; i<lt.size(); i++)
{
if (lt.isLeaf(i)) continue;
LispTree scanLT = lt.getBranch(i);
if (scanLT.keyword.equalsIgnoreCase("attributes"))
{
if (ltAttribute != null)
{
System.out.println("Multiple 'attributes' sections for a primitive (line " + scanLT.lineNumber + ")");
return true;
}
for(int j=0; j<scanLT.size(); j++)
{
if (scanLT.isLeaf(j)) continue;
LispTree subLT = scanLT.getBranch(j);
if (subLT.keyword.equalsIgnoreCase("name"))
{
if (subLT.size() != 1 || subLT.isBranch(0))
{
System.out.println("Primitive 'name' attribute should take a single atomic parameter (line " + subLT.lineNumber + ")");
return true;
}
primName = subLT.getLeaf(0);
continue;
}
if (subLT.keyword.equalsIgnoreCase("size"))
{
if (subLT.size() != 2 || subLT.isBranch(0) || subLT.isBranch(1))
{
System.out.println("Primitive 'size' attribute should take two atomic parameters (line " + subLT.lineNumber + ")");
return true;
}
primSizeX = subLT.getLeaf(0);
primSizeY = subLT.getLeaf(1);
continue;
}
}
ltAttribute = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("nets"))
{
if (ltNets != null)
{
System.out.println("Multiple 'nets' sections for a primitive (line " + scanLT.lineNumber + ")");
return true;
}
ltNets = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("ports"))
{
if (ltPorts != null)
{
System.out.println("Multiple 'ports' sections for a primitive (line " + scanLT.lineNumber + ")");
return true;
}
ltPorts = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("components"))
{
if (ltComponents != null)
{
System.out.println("Multiple 'components' sections for a primitive (line " + scanLT.lineNumber + ")");
return true;
}
ltComponents = scanLT;
continue;
}
}
// make sure a name and size were given
if (primName == null)
{
System.out.println("Missing 'name' attribute in primitive definition (line " + lt.lineNumber + ")");
return true;
}
if (primSizeX == null || primSizeY == null)
{
System.out.println("Missing 'size' attribute in primitive definition (line " + lt.lineNumber + ")");
return true;
}
// make the primitive
double sizeX = TextUtils.atof(primSizeX);
double sizeY = TextUtils.atof(primSizeY);
FPGANode primNP = new FPGANode(primName, this, sizeX, sizeY,
new Technology.NodeLayer []
{
new Technology.NodeLayer(componentLayer, 0, Poly.Type.CLOSED, Technology.NodeLayer.BOX, new Technology.TechPoint[] {
new Technology.TechPoint(EdgeH.makeLeftEdge(), EdgeV.makeBottomEdge()),
new Technology.TechPoint(EdgeH.makeRightEdge(), EdgeV.makeTopEdge()),
})
});
primNP.setLockedPrim();
defined = true;
// get ports
if (ltPorts != null)
{
// count ports
int portCount = 0;
for(int j=0; j<ltPorts.size(); j++)
{
if (ltPorts.isLeaf(j)) continue;
LispTree scanLT = ltPorts.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("port")) portCount++;
}
// create the ports
primNP.portList = new FPGAPort[portCount];
int portNumber = 0;
for(int j=0; j<ltPorts.size(); j++)
{
if (ltPorts.isLeaf(j)) continue;
LispTree scanLT = ltPorts.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("port"))
{
FPGAPort fp = new FPGAPort();
primNP.portList[portNumber] = fp;
if (makePrimPort(primNP, scanLT, fp, portNumber)) return true;
for(int k=0; k<portNumber; k++)
{
if (primNP.portList[k].name.equalsIgnoreCase(fp.name))
{
System.out.println("Duplicate port name: " + fp.name + " (line " + scanLT.lineNumber + ")");
return true;
}
}
portNumber++;
}
}
}
// get nets
if (ltNets != null)
{
// count the nets
int netCount = 0;
for(int j=0; j<ltNets.size(); j++)
{
if (ltNets.isLeaf(j)) continue;
LispTree scanLT = ltNets.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("net")) netCount++;
}
// create the nets
primNP.netList = new FPGANet[netCount];
int index = 0;
for(int j=0; j<ltNets.size(); j++)
{
if (ltNets.isLeaf(j)) continue;
LispTree scanLT = ltNets.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("net"))
{
primNP.netList[index] = new FPGANet();
if (makePrimNet(primNP, scanLT, primNP, primNP.netList[index])) return true;
index++;
}
}
}
// associate nets and ports
for(int k=0; k<primNP.numPorts(); k++)
{
FPGAPort fp = primNP.portList[k];
for(int i=0; i<primNP.numNets(); i++)
{
boolean found = false;
for(int j=0; j<primNP.netList[i].segFrom.length; j++)
{
if ((primNP.netList[i].segFrom[j].getX() == fp.posX && primNP.netList[i].segFrom[j].getY() == fp.posY) ||
(primNP.netList[i].segTo[j].getX() == fp.posX && primNP.netList[i].segTo[j].getY() == fp.posY))
{
fp.con = i;
found = true;
break;
}
}
if (found) break;
}
}
// create the ports on the primitive
PrimitivePort [] ports = new PrimitivePort[primNP.numPorts()];
for(int i=0; i<primNP.numPorts(); i++)
{
FPGAPort fp = primNP.portList[i];
fp.pp = PrimitivePort.newInstance(this, primNP, new ArcProto [] {wireArc}, fp.name, 0,180, fp.con,
fp.characteristic,EdgeH.fromCenter(fp.posX), EdgeV.fromCenter(fp.posY), EdgeH.fromCenter(fp.posX),
EdgeV.fromCenter(fp.posY));
ports[i] = fp.pp;
}
primNP.addPrimitivePortsFixed(ports);
// get pips
if (ltComponents != null)
{
// count the pips
int pipCount = 0;
for(int j=0; j<ltComponents.size(); j++)
{
if (ltComponents.isLeaf(j)) continue;
LispTree scanLT = ltComponents.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("pip")) pipCount++;
}
// create the pips
primNP.pipList = new FPGAPip[pipCount];
int i = 0;
for(int j=0; j<ltComponents.size(); j++)
{
if (ltComponents.isLeaf(j)) continue;
LispTree scanLT = ltComponents.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("pip"))
{
primNP.pipList[i] = new FPGAPip();
if (makePrimPip(primNP, scanLT, primNP, primNP.pipList[i])) return true;
i++;
}
}
}
return false;
}
/**
* Method to add a port to primitive "np" from the tree in "lt" and
* store information about it in the local structure "fp".
* Tree has "(port...)" structure. Returns true on error.
*/
private static boolean makePrimPort(PrimitiveNode np, LispTree lt, FPGAPort fp, int net)
{
// look for keywords
LispTree ltName = null, ltPosition = null, ltDirection = null;
for(int j=0; j<lt.size(); j++)
{
if (lt.isLeaf(j)) continue;
LispTree scanLT = lt.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name"))
{
ltName = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("position"))
{
ltPosition = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("direction"))
{
ltDirection = scanLT;
continue;
}
}
// validate
if (ltName == null)
{
System.out.println("Port has no name (line " + lt.lineNumber + ")");
return true;
}
if (ltName.size() != 1 || ltName.isBranch(0))
{
System.out.println("Port name must be a single atom (line " + ltName.lineNumber + ")");
return true;
}
fp.name = ltName.getLeaf(0);
if (ltPosition == null)
{
System.out.println("Port has no position (line " + lt.lineNumber + ")");
return true;
}
if (ltPosition.size() != 2 || ltPosition.isBranch(0) || ltPosition.isBranch(1))
{
System.out.println("Port position must be two atoms (line " + ltPosition.lineNumber + ")");
return true;
}
fp.posX = TextUtils.atof(ltPosition.getLeaf(0)) - np.getDefWidth()/2;
fp.posY = TextUtils.atof(ltPosition.getLeaf(1)) - np.getDefHeight()/2;
// determine directionality
fp.characteristic = PortCharacteristic.UNKNOWN;
if (ltDirection != null)
{
if (ltDirection.size() != 1 || ltDirection.isBranch(0))
{
System.out.println("Port direction must be a single atom (line " + ltDirection.lineNumber + ")");
return true;
}
String dir = ltDirection.getLeaf(0);
if (dir.equalsIgnoreCase("input")) fp.characteristic = PortCharacteristic.IN; else
if (dir.equalsIgnoreCase("output")) fp.characteristic = PortCharacteristic.OUT; else
if (dir.equalsIgnoreCase("bidir")) fp.characteristic = PortCharacteristic.BIDIR; else
{
System.out.println("Unknown port direction (line " + ltDirection.lineNumber + ")");
return true;
}
}
fp.con = net;
return false;
}
/**
* Method to add a net to primitive "np" from the tree in "lt" and store information
* about it in the local object "fNet".
* Tree has "(net...)" structure. Returns true on error.
*/
private static boolean makePrimNet(PrimitiveNode np, LispTree lt, FPGANode fn, FPGANet fNet)
{
// scan for information in the tree
fNet.name = null;
int segCount = 0;
Point2D [] seg = new Point2D[2];
for(int j=0; j<lt.size(); j++)
{
if (lt.isLeaf(j)) continue;
LispTree scanLT = lt.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name") && scanLT.size() == 1 && scanLT.isLeaf(0))
{
if (fNet.name != null)
{
System.out.println("Multiple names for network (line " + lt.lineNumber + ")");
return true;
}
fNet.name = scanLT.getLeaf(0);
continue;
}
if (scanLT.keyword.equalsIgnoreCase("segment"))
{
int pos = 0;
for(int i=0; i<2; i++)
{
// get end of net segment
if (scanLT.size() < pos+1)
{
System.out.println("Incomplete block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.isBranch(pos))
{
System.out.println("Must have atoms in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.getLeaf(pos).equalsIgnoreCase("coord"))
{
if (scanLT.size() < pos+3)
{
System.out.println("Incomplete block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.isBranch(pos+1) || scanLT.isBranch(pos+2))
{
System.out.println("Must have atoms in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
double x = TextUtils.atof(scanLT.getLeaf(pos+1)) - np.getDefWidth()/2;
double y = TextUtils.atof(scanLT.getLeaf(pos+2)) - np.getDefHeight()/2;
seg[i] = new Point2D.Double(x, y);
pos += 3;
} else if (scanLT.getLeaf(pos).equalsIgnoreCase("port"))
{
if (scanLT.size() < pos+2)
{
System.out.println("Incomplete block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.isBranch(pos+1))
{
System.out.println("Must have atoms in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
// find port
int found = -1;
for(int k=0; k<fn.numPorts(); k++)
{
if (fn.portList[k].name.equalsIgnoreCase(scanLT.getLeaf(pos+1)))
{
found = k;
break;
}
}
if (found < 0)
{
System.out.println("Unknown port on primitive net segment (line " + scanLT.lineNumber + ")");
return true;
}
double x = fn.portList[found].posX;
double y = fn.portList[found].posY;
seg[i] = new Point2D.Double(x, y);
pos += 2;
} else
{
System.out.println("Unknown keyword '" + scanLT.getLeaf(pos) +
"' in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
}
Point2D [] newFrom = new Point2D[segCount+1];
Point2D [] newTo = new Point2D[segCount+1];
for(int i=0; i<segCount; i++)
{
newFrom[i] = fNet.segFrom[i];
newTo[i] = fNet.segTo[i];
}
newFrom[segCount] = seg[0];
newTo[segCount] = seg[1];
fNet.segFrom = newFrom;
fNet.segTo = newTo;
segCount++;
continue;
}
}
return false;
}
/**
* Method to add a pip to primitive "np" from the tree in "lt" and save
* information about it in the local object "fpip".
* Tree has "(pip...)" structure. Returns true on error.
*/
private static boolean makePrimPip(PrimitiveNode np, LispTree lt, FPGANode fn, FPGAPip fPip)
{
// scan for information in this FPGAPIP object
fPip.name = null;
fPip.con1 = fPip.con2 = -1;
for(int j=0; j<lt.size(); j++)
{
if (lt.isLeaf(j)) continue;
LispTree scanLT = lt.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name") && scanLT.size() == 1 && scanLT.isLeaf(0))
{
if (fPip.name != null)
{
System.out.println("Multiple names for pip (line " + lt.lineNumber + ")");
return true;
}
fPip.name = scanLT.getLeaf(0);
continue;
}
if (scanLT.keyword.equalsIgnoreCase("position") && scanLT.size() == 2 &&
scanLT.isLeaf(0) && scanLT.isLeaf(1))
{
fPip.posX = TextUtils.atof(scanLT.getLeaf(0)) - np.getDefWidth()/2;
fPip.posY = TextUtils.atof(scanLT.getLeaf(1)) - np.getDefHeight()/2;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("connectivity") && scanLT.size() == 2 &&
scanLT.isLeaf(0) && scanLT.isLeaf(1))
{
for(int i=0; i<fn.numNets(); i++)
{
if (fn.netList[i].name.equalsIgnoreCase(scanLT.getLeaf(0))) fPip.con1 = i;
if (fn.netList[i].name.equalsIgnoreCase(scanLT.getLeaf(1))) fPip.con2 = i;
}
continue;
}
}
return false;
}
/******************** ARCHITECTURE PARSING: LAYOUT ********************/
/**
* Method to scan the entire tree for block definitions and create them.
*/
private Cell placePrimitives(LispTree lt)
{
// look through top level for the "blockdef"s
Cell topLevel = null;
for(int i=0; i<lt.size(); i++)
{
if (lt.isLeaf(i)) continue;
LispTree subLT = lt.getBranch(i);
if (!subLT.keyword.equalsIgnoreCase("blockdef") &&
!subLT.keyword.equalsIgnoreCase("architecture")) continue;
// create the primitive
Cell np = makeCell(subLT);
if (np == null) return null;
if (subLT.keyword.equalsIgnoreCase("architecture")) topLevel = np;
}
return topLevel;
}
/**
* Method to create a cell from a subtree "lt".
* Tree has "(blockdef...)" or "(architecture...)" structure.
* Returns nonzero on error.
*/
private Cell makeCell(LispTree lt)
{
// find all of the pieces of this block
LispTree ltAttribute = null, ltNets = null, ltPorts = null, ltComponents = null;
for(int i=0; i<lt.size(); i++)
{
if (lt.isLeaf(i)) continue;
LispTree scanLT = lt.getBranch(i);
if (scanLT.keyword.equalsIgnoreCase("attributes"))
{
if (ltAttribute != null)
{
System.out.println("Multiple 'attributes' sections for a block (line " + lt.lineNumber + ")");
return null;
}
ltAttribute = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("nets"))
{
if (ltNets != null)
{
System.out.println("Multiple 'nets' sections for a block (line " + lt.lineNumber + ")");
return null;
}
ltNets = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("ports"))
{
if (ltPorts != null)
{
System.out.println("Multiple 'ports' sections for a block (line " + lt.lineNumber + ")");
return null;
}
ltPorts = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("components"))
{
if (ltComponents != null)
{
System.out.println("Multiple 'components' sections for a block (line " + lt.lineNumber + ")");
return null;
}
ltComponents = scanLT;
continue;
}
}
// scan the attributes section
if (ltAttribute == null)
{
System.out.println("Missing 'attributes' sections on a block (line " + lt.lineNumber + ")");
return null;
}
String blockName = null;
boolean gotSize = false;
double sizeX = 0, sizeY = 0;
for(int j=0; j<ltAttribute.size(); j++)
{
if (ltAttribute.isLeaf(j)) continue;
LispTree scanLT = ltAttribute.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name"))
{
if (scanLT.size() != 1 || scanLT.isBranch(0))
{
System.out.println("Block 'name' attribute should take a single atomic parameter (line " + scanLT.lineNumber + ")");
return null;
}
blockName = scanLT.getLeaf(0);
continue;
}
if (scanLT.keyword.equalsIgnoreCase("size") && scanLT.size() == 2 &&
scanLT.isLeaf(0) && scanLT.isLeaf(1))
{
gotSize = true;
sizeX = TextUtils.atof(scanLT.getLeaf(0));
sizeY = TextUtils.atof(scanLT.getLeaf(1));
continue;
}
}
// validate
if (blockName == null)
{
System.out.println("Missing 'name' attribute in block definition (line " + ltAttribute.lineNumber + ")");
return null;
}
// make the cell
Cell cell = Cell.newInstance(Library.getCurrent(), blockName);
if (cell == null) return null;
System.out.println("Creating cell '" + blockName + "'");
// force size by placing pins in the corners
if (gotSize)
{
NodeInst.makeInstance(wirePinNode, new Point2D.Double(0.5, 0.5), 1, 1, cell);
NodeInst.makeInstance(wirePinNode, new Point2D.Double(sizeX-0.5, 0.5), 1, 1, cell);
NodeInst.makeInstance(wirePinNode, new Point2D.Double(0.5, sizeY-0.5), 1, 1, cell);
NodeInst.makeInstance(wirePinNode, new Point2D.Double(sizeX-0.5, sizeY-0.5), 1, 1, cell);
}
// add any unrecognized attributes
for(int j=0; j<ltAttribute.size(); j++)
{
if (ltAttribute.isLeaf(j)) continue;
LispTree scanLT = ltAttribute.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name")) continue;
if (scanLT.keyword.equalsIgnoreCase("size")) continue;
if (scanLT.size() != 1 || scanLT.isBranch(0))
{
System.out.println("Attribute '" + scanLT.keyword + "' attribute should take a single atomic parameter (line " +
scanLT.lineNumber + ")");
return null;
}
cell.newVar(scanLT.keyword, scanLT.getLeaf(0));
}
// place block components
if (ltComponents != null)
{
for(int j=0; j<ltComponents.size(); j++)
{
if (ltComponents.isLeaf(j)) continue;
LispTree scanLT = ltComponents.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("repeater"))
{
if (makeBlockRepeater(cell, scanLT)) return null;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("instance"))
{
if (makeBlockInstance(cell, scanLT)) return null;
continue;
}
}
}
// place block ports
if (ltPorts != null)
{
for(int j=0; j<ltPorts.size(); j++)
{
if (ltPorts.isLeaf(j)) continue;
LispTree scanLT = ltPorts.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("port"))
{
if (makeBlockPort(cell, scanLT)) return null;
}
}
}
// place block nets
if (ltNets != null)
{
// read the block nets
for(int j=0; j<ltNets.size(); j++)
{
if (ltNets.isLeaf(j)) continue;
LispTree scanLT = ltNets.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("net"))
{
if (makeBlockNet(cell, scanLT)) return null;
}
}
}
return cell;
}
/**
* Method to place an instance in cell "cell" from the LISPTREE in "lt".
* Tree has "(instance...)" structure. Returns true on error.
*/
private boolean makeBlockInstance(Cell cell, LispTree lt)
{
// scan for information in this block instance object
LispTree ltType = null, ltName = null, ltPosition = null, ltRotation = null, ltAttribute = null;
for(int i=0; i<lt.size(); i++)
{
if (lt.isLeaf(i)) continue;
LispTree scanLT = lt.getBranch(i);
if (scanLT.keyword.equalsIgnoreCase("type"))
{
if (ltType != null)
{
System.out.println("Multiple 'type' sections for a block (line " + lt.lineNumber + ")");
return true;
}
ltType = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("name"))
{
if (ltName != null)
{
System.out.println("Multiple 'name' sections for a block (line " + lt.lineNumber + ")");
return true;
}
ltName = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("position"))
{
if (ltPosition != null)
{
System.out.println("Multiple 'position' sections for a block (line " + lt.lineNumber + ")");
return true;
}
ltPosition = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("rotation"))
{
if (ltRotation != null)
{
System.out.println("Multiple 'rotation' sections for a block (line " + lt.lineNumber + ")");
return true;
}
ltRotation = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("attributes"))
{
if (ltAttribute != null)
{
System.out.println("Multiple 'attributes' sections for a block (line " + lt.lineNumber + ")");
return true;
}
ltAttribute = scanLT;
continue;
}
}
// validate
if (ltType == null)
{
System.out.println("No 'type' specified for block instance (line " + lt.lineNumber + ")");
return true;
}
if (ltType.size() != 1 || ltType.isBranch(0))
{
System.out.println("Need one atom in 'type' of block instance (line " + ltType.lineNumber + ")");
return true;
}
NodeProto np = findNodeProto(ltType.getLeaf(0));
if (np == null) np = cell.getLibrary().findNodeProto(ltType.getLeaf(0));
if (np == null)
{
System.out.println("Cannot find block type '" + ltType.getLeaf(0) + "' (line " + ltType.lineNumber + ")");
return true;
}
if (ltPosition == null)
{
System.out.println("No 'position' specified for block instance (line " + lt.lineNumber + ")");
return true;
}
if (ltPosition.size() != 2 || ltPosition.isBranch(0) || ltPosition.isBranch(1))
{
System.out.println("Need two atoms in 'position' of block instance (line " + ltPosition.lineNumber + ")");
return true;
}
int rotation = 0;
if (ltRotation != null)
{
if (ltRotation.size() != 1 || ltRotation.isBranch(0))
{
System.out.println("Need one atom in 'rotation' of block instance (line " + ltRotation.lineNumber + ")");
return true;
}
rotation = TextUtils.atoi(ltRotation.getLeaf(0)) * 10;
}
// name the instance if one is given
String nodeName = null;
if (ltName != null)
{
if (ltName.size() != 1 || ltName.isBranch(0))
{
System.out.println("Need one atom in 'name' of block instance (line " + ltName.lineNumber + ")");
return true;
}
nodeName = ltName.getLeaf(0);
}
// place the instance
double posX = TextUtils.atof(ltPosition.getLeaf(0));
double posY = TextUtils.atof(ltPosition.getLeaf(1));
double wid = np.getDefWidth();
double hei = np.getDefHeight();
if (np instanceof PrimitiveNode)
{
posX += wid/2;
posY += hei/2;
}
Point2D ctr = new Point2D.Double(posX, posY);
Orientation orient = Orientation.fromAngle(rotation);
NodeInst ni = NodeInst.makeInstance(np, ctr, wid, hei, cell, orient, nodeName);
if (ni == null) return true;
// add any attributes
if (ltAttribute != null)
{
for(int i=0; i<ltAttribute.size(); i++)
{
if (ltAttribute.isLeaf(i)) continue;
LispTree scanLT = ltAttribute.getBranch(i);
if (scanLT.size() != 1 || scanLT.isBranch(0))
{
System.out.println("Attribute '" + scanLT.keyword+ "' attribute should take a single atomic parameter (line " + lt.lineNumber + ")");
return true;
}
ni.newVar(scanLT.keyword, scanLT.getLeaf(0));
}
}
return false;
}
/**
* Method to add a port to block "cell" from the tree in "lt".
* Tree has "(port...)" structure. Returns true on error.
*/
private boolean makeBlockPort(Cell cell, LispTree lt)
{
LispTree ltName = null, ltPosition = null;
for(int j=0; j<lt.size(); j++)
{
if (lt.isLeaf(j)) continue;
LispTree scanLT = lt.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name"))
{
ltName = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("position"))
{
ltPosition = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("direction"))
{
continue;
}
}
// make the port
if (ltName == null)
{
System.out.println("Port has no name (line " + lt.lineNumber + ")");
return true;
}
if (ltName.size() != 1 || ltName.isBranch(0))
{
System.out.println("Port name must be a single atom (line " + ltName.lineNumber + ")");
}
if (ltPosition == null)
{
System.out.println("Port has no position (line " + lt.lineNumber + ")");
return true;
}
if (ltPosition.size() != 2 || ltPosition.isBranch(0) || ltPosition.isBranch(1))
{
System.out.println("Port position must be two atoms (line " + ltPosition.lineNumber + ")");
}
// create the structure
double posX = TextUtils.atof(ltPosition.getLeaf(0));
double posY = TextUtils.atof(ltPosition.getLeaf(1));
NodeInst ni = NodeInst.makeInstance(wirePinNode, new Point2D.Double(posX, posY), 0, 0, cell);
if (ni == null)
{
System.out.println("Error creating pin for port '" + ltName.getLeaf(0) + "' (line " + lt.lineNumber + ")");
return true;
}
PortInst pi = ni.getOnlyPortInst();
Export e = Export.newInstance(cell, pi, ltName.getLeaf(0));
if (e == null)
{
System.out.println("Error creating port '" + ltName.getLeaf(0) + "' (line " + lt.lineNumber + ")");
return true;
}
return false;
}
/**
* Method to place a repeater in cell "cell" from the LISPTREE in "lt".
* Tree has "(repeater...)" structure. Returns true on error.
*/
private boolean makeBlockRepeater(Cell cell, LispTree lt)
{
LispTree ltName = null, ltPortA = null, ltPortB = null;
for(int j=0; j<lt.size(); j++)
{
if (lt.isLeaf(j)) continue;
LispTree scanLT = lt.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name"))
{
ltName = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("porta"))
{
ltPortA = scanLT;
continue;
}
if (scanLT.keyword.equalsIgnoreCase("portb"))
{
ltPortB = scanLT;
continue;
}
}
// make the repeater
if (ltPortA == null)
{
System.out.println("Repeater has no 'porta' (line " + lt.lineNumber + ")");
return true;
}
if (ltPortA.size() != 2 || ltPortA.isBranch(0) || ltPortA.isBranch(1))
{
System.out.println("Repeater 'porta' position must be two atoms (line " + ltPortA.lineNumber + ")");
}
if (ltPortB == null)
{
System.out.println("Repeater has no 'portb' (line " + lt.lineNumber + ")");
return true;
}
if (ltPortB.size() != 2 || ltPortB.isBranch(0) || ltPortB.isBranch(1))
{
System.out.println("Repeater 'portb' position must be two atoms (line " + ltPortB.lineNumber + ")");
}
// name the repeater if one is given
String name = null;
if (ltName != null)
{
if (ltName.size() != 1 || ltName.isBranch(0))
{
System.out.println("Need one atom in 'name' of block repeater (line " + ltName.lineNumber + ")");
return true;
}
name = ltName.getLeaf(0);
}
// create the repeater
double portAX = TextUtils.atof(ltPortA.getLeaf(0));
double portAY = TextUtils.atof(ltPortA.getLeaf(1));
double portBX = TextUtils.atof(ltPortB.getLeaf(0));
double portBY = TextUtils.atof(ltPortB.getLeaf(1));
int angle = GenMath.figureAngle(new Point2D.Double(portAX, portAY), new Point2D.Double(portBX, portBY));
Point2D ctr = new Point2D.Double((portAX + portBX) / 2, (portAY + portBY) / 2);
Orientation orient = Orientation.fromAngle(angle);
NodeInst ni = NodeInst.makeInstance(repeaterNode, ctr, 10,3, cell, orient, name);
if (ni == null)
{
System.out.println("Error creating repeater (line " + lt.lineNumber + ")");
return true;
}
return false;
}
/**
* Method to extract block net information from the LISPTREE in "lt".
* Tree has "(net...)" structure. Returns true on error.
*/
private boolean makeBlockNet(Cell cell, LispTree lt)
{
// find the net name
for(int j=0; j<lt.size(); j++)
{
if (lt.isLeaf(j)) continue;
LispTree scanLT = lt.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("name"))
{
if (scanLT.size() != 1 || scanLT.isBranch(0))
{
System.out.println("Net name must be a single atom (line " + scanLT.lineNumber + ")");
return true;
}
continue;
}
}
// scan for segment objects
for(int j=0; j<lt.size(); j++)
{
if (lt.isLeaf(j)) continue;
LispTree scanLT = lt.getBranch(j);
if (scanLT.keyword.equalsIgnoreCase("segment"))
{
int pos = 0;
NodeInst [] nis = new NodeInst[2];
PortProto [] pps = new PortProto[2];
for(int i=0; i<2; i++)
{
// get end of arc
if (scanLT.size() < pos+1)
{
System.out.println("Incomplete block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.isBranch(pos))
{
System.out.println("Must have atoms in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.getLeaf(pos).equalsIgnoreCase("component"))
{
if (scanLT.size() < pos+3)
{
System.out.println("Incomplete block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.isBranch(pos+1) || scanLT.isBranch(pos+2))
{
System.out.println("Must have atoms in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
// find component and port
NodeInst niFound = null;
String name = scanLT.getLeaf(pos+1);
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.getName().equalsIgnoreCase(name))
{
niFound = ni;
break;
}
}
if (niFound == null)
{
System.out.println("Cannot find component '" + scanLT.getLeaf(pos+1) +
"' in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
nis[i] = niFound;
pps[i] = niFound.getProto().findPortProto(scanLT.getLeaf(pos+2));
if (pps[i] == null)
{
System.out.println("Cannot find port '" + scanLT.getLeaf(pos+2) +
"' on component '" + scanLT.getLeaf(pos+1) +
"' in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
pos += 3;
} else if (scanLT.getLeaf(pos).equalsIgnoreCase("coord"))
{
if (scanLT.size() < pos+3)
{
System.out.println("Incomplete block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.isBranch(pos+1) || scanLT.isBranch(pos+2))
{
System.out.println("Must have atoms in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
double x = TextUtils.atof(scanLT.getLeaf(pos+1));
double y = TextUtils.atof(scanLT.getLeaf(pos+2));
Rectangle2D search = new Rectangle2D.Double(x, y, 0, 0);
// find pin at this point
NodeInst niFound = null;
for(Iterator<RTBounds> it = cell.searchIterator(search); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
if (ni.getProto() != wirePinNode) continue;
if (ni.getTrueCenterX() == x && ni.getTrueCenterY() == y)
{
niFound = ni;
break;
}
}
if (niFound == null)
{
niFound = NodeInst.makeInstance(wirePinNode, new Point2D.Double(x, y), 0, 0, cell);
if (niFound == null)
{
System.out.println("Cannot create pin for block net segment (line " + scanLT.lineNumber + ")");
return true;
}
}
nis[i] = niFound;
pps[i] = niFound.getProto().getPort(0);
pos += 3;
} else if (scanLT.getLeaf(pos).equalsIgnoreCase("port"))
{
if (scanLT.size() < pos+2)
{
System.out.println("Incomplete block net segment (line " + scanLT.lineNumber + ")");
return true;
}
if (scanLT.isBranch(pos+1))
{
System.out.println("Must have atoms in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
// find port
Export pp = cell.findExport(scanLT.getLeaf(pos+1));
if (pp == null)
{
System.out.println("Cannot find port '" + scanLT.getLeaf(pos+1) +
"' in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
pps[i] = pp.getOriginalPort().getPortProto();
nis[i] = pp.getOriginalPort().getNodeInst();
pos += 2;
} else
{
System.out.println("Unknown keyword '" + scanLT.getLeaf(pos) +
"' in block net segment (line " + scanLT.lineNumber + ")");
return true;
}
}
// now create the arc
PortInst pi0 = nis[0].findPortInstFromProto(pps[0]);
PortInst pi1 = nis[1].findPortInstFromProto(pps[1]);
ArcInst ai = ArcInst.makeInstanceBase(wireArc, 0, pi0, pi1);
if (ai == null)
{
System.out.println("Cannot run segment (line " + scanLT.lineNumber + ")");
return true;
}
}
}
return false;
}
}