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

com.sun.electric.technology.technologies.FPGA Maven / Gradle / Ivy

/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: FPGA.java
 * FPGA, a customizable technology.
 * Written by Steven M. Rubin
 *
 * 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.technology.technologies;

import com.sun.electric.database.EditingPreferences;
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.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.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.Geometric;
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.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
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 com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.GenMath;
import com.sun.electric.util.math.Orientation;

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,
			new Technology.NodeLayer []
			{
				new Technology.NodeLayer(wireLayer, 0, Poly.Type.DISC, Technology.NodeLayer.POINTS, new Technology.TechPoint [] {
					new Technology.TechPoint(EdgeH.c(0), EdgeV.c(0)),
					new Technology.TechPoint(EdgeH.r(0.5), EdgeV.c(0))})
			});
		wirePinNode.addPrimitivePorts(
				PrimitivePort.single(wirePinNode, new ArcProto[] {wireArc}, "wire", 0,180, 0, PortCharacteristic.UNKNOWN,
                    EdgeH.c(0), EdgeV.c(0), EdgeH.c(0), EdgeV.c(0))
			);
		wirePinNode.setFunction(PrimitiveNode.Function.PIN);
		wirePinNode.setSquare();
		wirePinNode.setWipeOn1or2();

		/** pip */
		pipNode = PrimitiveNode.newInstance("Pip", this, 2, 2,
			new Technology.NodeLayer []
			{
				new Technology.NodeLayer(pipLayer, 0, Poly.Type.FILLED, Technology.NodeLayer.BOX, new Technology.TechPoint [] {
					new Technology.TechPoint(EdgeH.l(-1), EdgeV.b(-1)),
					new Technology.TechPoint(EdgeH.r(1), EdgeV.t(1))})
			});
		pipNode.addPrimitivePorts(
				PrimitivePort.single(pipNode, new ArcProto[] {wireArc}, "pip", 0,180, 0, PortCharacteristic.UNKNOWN,
                    EdgeH.c(0), EdgeV.c(0), EdgeH.c(0), EdgeV.c(0))
			);
		pipNode.setFunction(PrimitiveNode.Function.CONNECT);
		pipNode.setSquare();

		/** repeater */
		repeaterNode = PrimitiveNode.newInstance("Repeater", this, 10, 3,
			new Technology.NodeLayer []
			{
				new Technology.NodeLayer(repeaterLayer, 0, Poly.Type.FILLED, Technology.NodeLayer.BOX, new Technology.TechPoint [] {
					new Technology.TechPoint(EdgeH.l(-5), EdgeV.b(-1.5)),
					new Technology.TechPoint(EdgeH.r(5), EdgeV.t(1.5))})
			});
		repeaterNode.addPrimitivePorts(
				PrimitivePort.newInstance(repeaterNode, new ArcProto[] {wireArc}, "a", 180,45, 0, PortCharacteristic.UNKNOWN,
                    EdgeH.l(-5), EdgeV.c(0), EdgeH.l(-5), EdgeV.c(0)),
				PrimitivePort.newInstance(repeaterNode, new ArcProto[] {wireArc}, "b", 0,45, 1, PortCharacteristic.UNKNOWN,
                    EdgeH.r(5), EdgeV.c(0), EdgeH.r(5), EdgeV.c(0))
			);
		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 values;

		LispTree()
		{
			values = new ArrayList();
		}

		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, defWidth, defHeight, 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 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 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 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 it = ni.getExports(); it.hasNext(); )
					{
						Export opp = it.next();
						Network oNet = nl.getNetwork(opp, 0);
						if (oNet != net) continue;

						for(Iterator 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 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) continue;
				boolean pathGood = true;
				for(int j=0; j= 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, EditingPreferences ep)
	{
		// look through top level for the "primdef"s
		int total = 0;
		for(int i=0; i 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 it = cell.searchIterator(search); it.hasNext(); )
						{
							Geometric 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, ep, 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, ep, 0, pi0, pi1);
				if (ai == null)
				{
					System.out.println("Cannot run segment (line " + scanLT.lineNumber + ")");
					return true;
				}
			}
		}
		return false;
	}

}