/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LESizer.java
* Written by: Jonathan Gainsley, Sun Microsystems.
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*
* Created on November 11, 2003, 4:42 PM
*/
package com.sun.electric.tool.logicaleffort;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.util.concurrent.utils.ElapseTimer;
import com.sun.electric.util.TextUtils;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* LESizer sizes an LENetlist. The LENetlist is generated by LENetlister from
* the Electric database, or perhaps read in from a Spice file(?)
*
* NOTE: the only 'Electric' objects used are in LENetlister, any objects
* referenced in this file are from the logicaleffort package, although their
* names may imply otherwise. Their names are as such because their names match
* PNP's naming scheme.
*
* @author gainsley
*/
public class LESizer {
/** which algorithm to use */
private Alg optimizationAlg;
/** Where to direct output */
private PrintStream out;
/** What job we are part of */
private Job job;
/** Netlist */
private LENetlister1 netlist;
/** error logger */
private ErrorLogger errorLogger;
/** Alg is a typesafe enum class that describes the algorithm to be used */
public static enum Alg {
/** Sizes all gates for user specified equal gate delay */
EQUALGATEDELAYS("Equal Gate Delays"),
/** Sizes for optimal path delay */
PATHDELAY("Path Delay");
private final String name;
private Alg(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
/** Creates a new instance of LESizer */
protected LESizer(Alg alg, LENetlister1 netlist, Job job, ErrorLogger errorLogger) {
optimizationAlg = alg;
this.netlist = netlist;
this.job = job;
this.errorLogger = errorLogger;
out = new PrintStream(System.out);
}
// ============================ Sizing For Equal Gate Delays
// ==========================
/**
* Optimize using loop algorithm;
*
* @param maxDeltaX
* maximum tolerance allowed in X
* @param N
* maximum number of loops
* @param verbose
* print out size information for each optimization loop
* @return true if succeeded, false otherwise
*
* Optimization will stop when the difference in sizes (X) is less
* than maxDeltaX, or when N iterations have occurred.
*/
protected boolean optimizeLoops(float maxDeltaX, int N, boolean verbose, float alpha, float keeperRatio) {
// iterate through all the instances, updating sizes
float currentLoopDeltaX = maxDeltaX + 1; // force at least one iteration
float lastLoopDeltaX = currentLoopDeltaX;
int divergingIters = 0; // count number if iterations sizing is
// diverging
ElapseTimer timer = ElapseTimer.createInstance();
int loopcount = 0;
while ((currentLoopDeltaX > maxDeltaX) && (loopcount < N)) {
// check for aborted state of job
if (((LETool.AnalyzeCell) job).checkAbort(null))
return false;
currentLoopDeltaX = 0;
timer.start();
System.out.print(" Iteration " + loopcount);
if (verbose)
System.out.println(":");
// iterate through each instance
Iterator<Instance> instancesIter = netlist.getAllInstances().values().iterator();
while (instancesIter.hasNext()) {
Instance instance = instancesIter.next();
String instanceName = instance.getName();
// make sure it is a sizeable gate
if (instance.isLeGate()) {
// get output pin (do not iterate over all output pins, does
// not make sense)
ArrayList<Pin> outputPins = instance.getOutputPins();
if (outputPins.size() != 1) {
// error
continue;
}
Pin outputPin = outputPins.get(0);
Net net = outputPin.getNet();
// now find all pins connected to this net
ArrayList<Pin> netpins = net.getAllPins();
// find all drivers in same group, of same type (LEGATe or
// LEKEEPER)
List<Instance> drivers = new ArrayList<Instance>();
List<Instance> arrayedDrivers = new ArrayList<Instance>();
for (Pin pin : netpins) {
// only interested in drivers
if (pin.getDir() != Pin.Dir.OUTPUT)
continue;
Instance inst = pin.getInstance();
if (inst.getType() == instance.getType()) {
if (inst.getParallelGroup() == instance.getParallelGroup()) {
// add the instance. Note this adds the current
// instance at some point as well
drivers.add(inst);
// error check
if (inst.getParallelGroup() > 0 && loopcount == 0
&& inst.getLeSU() != instance.getLeSU()) {
String msg = "\nError: LEGATE \"" + inst.getName()
+ "\" drives in parallel with \"" + instance.getName()
+ "\" but has a different step-up";
System.out.println(msg);
NodeInst ni = inst.getNodable().getNodeInst();
if (ni != null) {
errorLogger.logError(msg, ni, ni.getParent(), inst.getContext(), 0);
}
}
}
}
if ((inst.getNodable().getNodeInst() == instance.getNodable().getNodeInst())
&& (inst.getContext() == instance.getContext())) {
// this must be an arrayed driver: not this also
// adds current instance at some point as well
arrayedDrivers.add(inst);
}
}
// this will be the new size.
float newX = 0;
// if this is an LEKEEPER, we need to find smallest gate (or
// group)
// that also drives this net, it is assumed that will have
// to overpower this keeper
if (instance.getType() == Instance.Type.LEKEEPER) {
Map<String, List<Instance>> drivingGroups = new HashMap<String, List<Instance>>();
float smallestX = 0;
// iterate over all drivers on net
for (Pin pin : netpins) {
// only interested in drivers
if (pin.getDir() != Pin.Dir.OUTPUT)
continue;
Instance inst = pin.getInstance();
// if ((inst.getType() == Instance.Type.LEGATE) ||
// (inst.getType() == Instance.Type.STATICGATE)) {
if ((inst.getType() == Instance.Type.LEGATE)) {
// organize by groups
int i = inst.getParallelGroup();
Integer integer = new Integer(i);
if (i <= 0) {
// this gate drives independently, check
// size
if (smallestX == 0)
smallestX = inst.getLeX();
if (inst.getLeX() < smallestX)
smallestX = inst.getLeX();
}
// add to group to sum up drive strength later
List<Instance> groupList = drivingGroups.get(integer.toString());
if (groupList == null) {
groupList = new ArrayList<Instance>();
drivingGroups.put(integer.toString(), groupList);
}
groupList.add(inst);
}
}
// find smallest total size of groups
Set<String> keys = drivingGroups.keySet();
for (String str : keys) {
List<Instance> groupList = drivingGroups.get(str);
if (groupList == null)
continue; // skip empty groups
// get size
float sizeX = 0;
for (Instance inst : groupList) {
sizeX += inst.getLeX();
}
// check size of group
if (smallestX == 0)
smallestX = sizeX;
if (sizeX < smallestX)
smallestX = sizeX;
}
// if no drivers found, issue warning
if (!keys.iterator().hasNext() && loopcount == 0) {
String msg = "\nError: LEKEEPER \"" + instance.getName()
+ "\" does not fight against any drivers";
System.out.println(msg);
NodeInst ni = instance.getNodable().getNodeInst();
if (ni != null) {
errorLogger.logError(msg, ni, ni.getParent(), instance.getContext(), 0);
}
}
// For now, split effort equally amongst all drivers
if (instance.getParallelGroup() <= 0) {
newX = smallestX * netlist.getKeeperRatio() / arrayedDrivers.size();
} else {
newX = smallestX * netlist.getKeeperRatio() / drivers.size();
}
}
// If this is an LEGATE, simply sum all capacitances on the
// Net
if (instance.getType() == Instance.Type.LEGATE) {
// compute total le*X (totalcap)
float totalcap = 0;
Iterator<Pin> netpinsIter = netpins.iterator();
int numLoads = 0;
while (netpinsIter.hasNext()) {
Pin netpin = netpinsIter.next();
Instance netpinInstance = netpin.getInstance();
float load = netpinInstance.getLeX() * netpin.getLE() * (float) netpinInstance.getMfactor();
if (netpin.getDir() == Pin.Dir.OUTPUT)
load *= alpha;
totalcap += load;
// check to see if gate is only driving itself
if (netpinInstance != instance)
numLoads++;
}
// create error if no loads only on first iteration
if (numLoads == 0 && loopcount == 0) {
String msg = "\nError: LEGATE \"" + instance.getName() + "\" has no loads: will be ignored";
System.out.println(msg);
NodeInst ni = instance.getNodable().getNodeInst();
if (ni != null) {
errorLogger.logError(msg, ni, ni.getParent(), instance.getContext(), 1);
}
}
// ignore if no loads, on all iterations
if (numLoads == 0)
continue;
// For now, split effort equally amongst all drivers
// Group 0 drives individually
if (instance.getParallelGroup() <= 0) {
newX = totalcap / instance.getLeSU() / arrayedDrivers.size();
} else {
newX = totalcap / instance.getLeSU() / drivers.size();
}
// also take into account mfactor of driver
newX = newX / (float) instance.getMfactor();
}
// determine change in size
float currentX = instance.getLeX();
float deltaX;
if (currentX == 0 && newX == 0) {
// if before and after are 0, delta is 0
deltaX = 0f;
} else {
// account for divide by 0
if (currentX == 0)
currentX = 0.001f;
deltaX = Math.abs((newX - currentX) / currentX);
}
currentLoopDeltaX = (deltaX > currentLoopDeltaX) ? deltaX : currentLoopDeltaX;
if (verbose) {
out.println("Optimized " + instanceName + ": size: "
+ TextUtils.formatDouble(instance.getLeX(), 3) + "x ==> "
+ TextUtils.formatDouble(newX, 3) + "x");
}
instance.setLeX(newX);
} // if (leGate)
} // while (instancesIter)
// All done, print some statistics about this iteration
timer.end();
System.out.println(" ...done (" + timer + "), delta: " + currentLoopDeltaX);
if (verbose)
System.out.println("-----------------------------------");
loopcount++;
// check to see if we're diverging or not converging
if (currentLoopDeltaX >= lastLoopDeltaX) {
if (divergingIters > 2) {
System.out.println(" Sizing diverging, aborting");
return false;
}
divergingIters++;
}
lastLoopDeltaX = currentLoopDeltaX;
} // while (currentLoopDeltaX ... )
return true;
}
// ========================== Sizing for Path Optimization
// =====================
protected List getEndNets() {
List endNets = new ArrayList();
Iterator<Net> netIter = netlist.getAllNets().values().iterator();
while (netIter.hasNext()) {
Net net = netIter.next();
}
return null;
}
// =============================== Statistics
// ==================================
// ============================== Design Printing
// ===============================
/**
* Dump the design information for debugging purposes
*/
protected void printDesign() {
out.println("Instances in design are:");
Iterator<Instance> instancesIter = netlist.getAllInstances().values().iterator();
while (instancesIter.hasNext()) {
Instance instance = instancesIter.next();
String instanceName = instance.getName();
StringBuffer buf = new StringBuffer();
out.println("\t" + instanceName + " ==> " + TextUtils.formatDouble(instance.getLeX(), 3) + "x");
ArrayList<Pin> pins = instance.getAllPins();
// now print out pinname ==> netname
Iterator<Pin> pinsIter = pins.iterator();
while (pinsIter.hasNext()) {
Pin pin = pinsIter.next();
out.println("\t\t" + pin.getName() + " ==> " + pin.getNetName());
}
}
}
/**
* Generate simple size file (for regression purposes)
*
* @param filename
* output filename
*/
protected int printDesignSizes(String filename) {
// open output file
try {
FileWriter fileWriter = new FileWriter(filename); // throws
// IOException
// iterate through all instances
Iterator<Instance> instancesIter = netlist.getAllInstances().values().iterator();
while (instancesIter.hasNext()) {
Instance instance = instancesIter.next();
String instanceName = instance.getName();
float leX = instance.getLeX();
fileWriter.write(instanceName + " " + leX + "\n"); // throws
// IOException
fileWriter.flush(); // throws IOException
}
fileWriter.close(); // throws IOException
} catch (IOException e) {
out.println("Writing to file " + filename + ": " + e.getMessage());
return 1;
}
return 0;
}
/**
* Generate SKILL backannotation file
*
* @param filename
* output filename
* @param libname
* The Opus library name to be annotated
* @param cellname
* The Opus cell to be annotated
*/
protected int printDesignSkill(String filename, String libname, String cellname) {
// nothing here
return 0;
}
/**
* Dummy method to improve test coverage
*/
protected void testcoverage() {
// nothing here
}
// ---------------------------------------TEST---------------------------------------
// ---------------------------------------TEST---------------------------------------
/** run a contrived test */
public static void test1() {
System.out.println("Running GASP test circuit");
System.out.println("=========================");
float su = (float) 4.0;
LENetlister1 netlist = new LENetlister1(null, null);
{
// inv1
Pin pin_a = new Pin("A", Pin.Dir.INPUT, (float) 1.0, "nand1_out");
Pin pin_y = new Pin("Y", Pin.Dir.OUTPUT, (float) 1.0, "inv1_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_a);
pins.add(pin_y);
netlist.addInstance("inv1", Instance.Type.LEGATE, su, (float) 1.0, pins, null);
}
{
// inv2
Pin pin_a = new Pin("A", Pin.Dir.INPUT, (float) 1.0, "pu_out");
Pin pin_y = new Pin("Y", Pin.Dir.OUTPUT, (float) 1.0, "inv2_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_a);
pins.add(pin_y);
netlist.addInstance("inv2", Instance.Type.LEGATE, su, (float) 1.0, pins, null);
}
{
// inv3
Pin pin_a = new Pin("A", Pin.Dir.INPUT, (float) 1.0, "nand1_out");
Pin pin_y = new Pin("Y", Pin.Dir.OUTPUT, (float) 1.0, "inv3_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_a);
pins.add(pin_y);
netlist.addInstance("inv3", Instance.Type.LEGATE, su, (float) 1.0, pins, null);
}
{
// nand1
Pin pin_a = new Pin("A", Pin.Dir.INPUT, (float) 1.333, "inv2_out");
Pin pin_b = new Pin("B", Pin.Dir.INPUT, (float) 1.333, "pd_out");
Pin pin_y = new Pin("Y", Pin.Dir.OUTPUT, (float) 2.0, "nand1_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_a);
pins.add(pin_b);
pins.add(pin_y);
netlist.addInstance("nand1", Instance.Type.LEGATE, su, (float) 1.0, pins, null);
}
{
// pu
Pin pin_g = new Pin("G", Pin.Dir.INPUT, (float) 0.667, "nand1_out");
Pin pin_d = new Pin("D", Pin.Dir.OUTPUT, (float) 0.667, "pu_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_g);
pins.add(pin_d);
netlist.addInstance("pu", Instance.Type.LEGATE, su, (float) 1.0, pins, null);
}
{
// pd
Pin pin_g = new Pin("G", Pin.Dir.INPUT, (float) 0.333, "inv3_out");
Pin pin_d = new Pin("D", Pin.Dir.OUTPUT, (float) 0.333, "pd_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_g);
pins.add(pin_d);
netlist.addInstance("pd", Instance.Type.LEGATE, su, (float) 1.0, pins, null);
}
{
// cap1
Pin pin_c = new Pin("C", Pin.Dir.INPUT, (float) 1.0, "pd_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_c);
netlist.addInstance("cap1", Instance.Type.LOAD, su, (float) 0.0, pins, null);
}
{
// cap2
Pin pin_c = new Pin("C", Pin.Dir.INPUT, (float) 1.0, "pu_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_c);
netlist.addInstance("cap2", Instance.Type.LOAD, su, (float) 0.0, pins, null);
}
{
// cap3
Pin pin_c = new Pin("C", Pin.Dir.INPUT, (float) 1.0, "inv1_out");
ArrayList<Pin> pins = new ArrayList<Pin>();
pins.add(pin_c);
netlist.addInstance("cap3", Instance.Type.LOAD, su, (float) 100.0, pins, null);
}
netlist.getSizer().printDesign();
netlist.getSizer().optimizeLoops((float) 0.01, 30, true, (float) 0.7, (float) 0.1);
System.out.println("After optimization:");
netlist.getSizer().printDesign();
}
}