📄 spice.java
字号:
if (modelName != null) infstr.append(" " + modelName); // compute length and width (or area for nonMOS transistors) TransistorSize size = ni.getTransistorSize(context); if (size == null) System.out.println("Warning: transistor has null size " + ni.describe(false)); else { // write the length Double foundLen = null; Variable varLen = ni.getVar(Schematics.ATTR_LENGTH); if (varLen != null && varLen.getCode() == CodeExpression.Code.SPICE && !useCDL && Simulation.isSpiceUseCellParameters()) { // write as a parameter infstr.append(" L=" + evalParam(context, no, varLen, forceEval)); } else { // write the value, start by getting gate length subtraction in lambda double lengthSubtraction = layoutTechnology.getGateLengthSubtraction() / layoutTechnology.getScale() * 1000; if (size.getDoubleLength() > 0) { double l = maskScale * size.getDoubleLength(); l -= lengthSubtraction; // make into microns (convert to nanometers then divide by 1000) if (!Simulation.isSpiceWriteTransSizeInLambda()) l *= layoutTechnology.getScale() / 1000.0; if (fun == PrimitiveNode.Function.TRANMOS || fun == PrimitiveNode.Function.TRA4NMOS || fun == PrimitiveNode.Function.TRAPMOS || fun == PrimitiveNode.Function.TRA4PMOS || fun == PrimitiveNode.Function.TRADMOS || fun == PrimitiveNode.Function.TRA4DMOS || ((fun == PrimitiveNode.Function.TRANJFET || fun == PrimitiveNode.Function.TRAPJFET || fun == PrimitiveNode.Function.TRADMES || fun == PrimitiveNode.Function.TRAEMES) && (spiceEngine == Simulation.SpiceEngine.SPICE_ENGINE_H || spiceEngine == Simulation.SpiceEngine.SPICE_ENGINE_H_ASSURA))) { // schematic transistors may be text if (size.getDoubleLength() == 0 && size.getLength() instanceof String) { infstr.append(" L=" + formatParam((String)size.getLength(), TextDescriptor.Unit.DISTANCE, false)); if (lengthSubtraction != 0) infstr.append(" - " + lengthSubtraction); } else { infstr.append(" L=" + TextUtils.formatDouble(l)); if (!Simulation.isSpiceWriteTransSizeInLambda() && !st090laytrans) infstr.append("U"); } } foundLen = new Double(l); } else { // get gate length subtraction in lambda if (size.getDoubleLength() == 0 && size.getLength() instanceof String) { infstr.append(" L=" + formatParam((String)size.getLength(), TextDescriptor.Unit.DISTANCE, false)); if (lengthSubtraction != 0) infstr.append(" - " + lengthSubtraction); } } } // write the width Double foundWid = null; Variable varWid = ni.getVar(Schematics.ATTR_WIDTH); if (varWid != null && varWid.getCode() == CodeExpression.Code.SPICE && !useCDL && Simulation.isSpiceUseCellParameters()) { // write as a parameter infstr.append(" W=" + evalParam(context, no, varWid, forceEval)); } else { // write the value if (size.getDoubleWidth() > 0) { double w = maskScale * size.getDoubleWidth(); // make into microns (convert to nanometers then divide by 1000) if (!Simulation.isSpiceWriteTransSizeInLambda()) w *= layoutTechnology.getScale() / 1000.0; if (fun == PrimitiveNode.Function.TRANMOS || fun == PrimitiveNode.Function.TRA4NMOS || fun == PrimitiveNode.Function.TRAPMOS || fun == PrimitiveNode.Function.TRA4PMOS || fun == PrimitiveNode.Function.TRADMOS || fun == PrimitiveNode.Function.TRA4DMOS || ((fun == PrimitiveNode.Function.TRANJFET || fun == PrimitiveNode.Function.TRAPJFET || fun == PrimitiveNode.Function.TRADMES || fun == PrimitiveNode.Function.TRAEMES) && (spiceEngine == Simulation.SpiceEngine.SPICE_ENGINE_H || spiceEngine == Simulation.SpiceEngine.SPICE_ENGINE_H_ASSURA))) { if ((size.getDoubleWidth() == 0) && (size.getWidth() instanceof String)) { infstr.append(" W="+formatParam((String)size.getWidth(), TextDescriptor.Unit.DISTANCE, false)); } else { infstr.append(" W=" + TextUtils.formatDouble(w)); if (!Simulation.isSpiceWriteTransSizeInLambda() && !st090laytrans) infstr.append("U"); } } foundWid = new Double(w); } else { // get gate length subtraction in lambda if (size.getDoubleWidth() == 0 && size.getWidth() instanceof String) infstr.append(" W="+formatParam((String)size.getWidth(), TextDescriptor.Unit.DISTANCE, false)); } } // write area if appropriate if (fun != PrimitiveNode.Function.TRANMOS && fun != PrimitiveNode.Function.TRA4NMOS && fun != PrimitiveNode.Function.TRAPMOS && fun != PrimitiveNode.Function.TRA4PMOS && fun != PrimitiveNode.Function.TRADMOS && fun != PrimitiveNode.Function.TRA4DMOS) { if (foundLen != null && foundWid != null) { infstr.append(" AREA=" + TextUtils.formatDouble(foundLen.doubleValue()*foundWid.doubleValue())); if (!Simulation.isSpiceWriteTransSizeInLambda()) infstr.append("P"); } } } // make sure transistor is connected to nets SpiceNet spNetGate = spiceNetMap.get(gateNet); SpiceNet spNetSource = spiceNetMap.get(sourceNet); SpiceNet spNetDrain = spiceNetMap.get(drainNet); if (spNetGate == null || spNetSource == null || spNetDrain == null) continue; // compute area of source and drain if (!useCDL) { if (fun == PrimitiveNode.Function.TRANMOS || fun == PrimitiveNode.Function.TRA4NMOS || fun == PrimitiveNode.Function.TRAPMOS || fun == PrimitiveNode.Function.TRA4PMOS || fun == PrimitiveNode.Function.TRADMOS || fun == PrimitiveNode.Function.TRA4DMOS) { double as = 0, ad = 0, ps = 0, pd = 0; if (spNetSource.transistorCount != 0) { as = spNetSource.diffArea / spNetSource.transistorCount; ps = spNetSource.diffPerim / spNetSource.transistorCount; if (!Simulation.isSpiceWriteTransSizeInLambda()) { as *= layoutTechnology.getScale() * layoutTechnology.getScale() / 1000000.0; ps *= layoutTechnology.getScale() / 1000.0; } } if (spNetDrain.transistorCount != 0) { ad = spNetDrain.diffArea / spNetDrain.transistorCount; pd = spNetDrain.diffPerim / spNetDrain.transistorCount; if (!Simulation.isSpiceWriteTransSizeInLambda()) { ad *= layoutTechnology.getScale() * layoutTechnology.getScale() / 1000000.0; pd *= layoutTechnology.getScale() / 1000.0; } } if (as > 0.0) { infstr.append(" AS=" + TextUtils.formatDouble(as)); if (!Simulation.isSpiceWriteTransSizeInLambda() && !st090laytrans) infstr.append("P"); } if (ad > 0.0) { infstr.append(" AD=" + TextUtils.formatDouble(ad)); if (!Simulation.isSpiceWriteTransSizeInLambda() && !st090laytrans) infstr.append("P"); } if (ps > 0.0) { infstr.append(" PS=" + TextUtils.formatDouble(ps)); if (!Simulation.isSpiceWriteTransSizeInLambda() && !st090laytrans) infstr.append("U"); } if (pd > 0.0) { infstr.append(" PD=" + TextUtils.formatDouble(pd)); if (!Simulation.isSpiceWriteTransSizeInLambda() && !st090laytrans) infstr.append("U"); } } } // Writing MFactor if available. writeMFactor(context, ni, infstr); infstr.append("\n"); multiLinePrint(false, infstr.toString()); } // print resistances and capacitances if (segmentedNets != null) { if (spLevel == Simulation.SpiceParasitics.RC_PROXIMITY) { parasiticInfo.writeNewSpiceCode(cell, cni, layoutTechnology, this); } else { // write caps int capCount = 0; multiLinePrint(true, "** Extracted Parasitic Capacitors ***\n"); for (SpiceSegmentedNets.NetInfo netInfo : segmentedNets.getUniqueSegments()) { if (netInfo.getCap() > cell.getTechnology().getMinCapacitance()) { if (netInfo.getName().equals("gnd")) continue; // don't write out caps from gnd to gnd multiLinePrint(false, "C" + capCount + " " + netInfo.getName() + " 0 " + TextUtils.formatDouble(netInfo.getCap()) + "fF\n"); capCount++; } } // write resistors int resCount = 0; multiLinePrint(true, "** Extracted Parasitic Resistors ***\n"); for (Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); ) { ArcInst ai = it.next(); Double res = segmentedNets.getRes(ai); if (res == null) continue; String n0 = segmentedNets.getNetName(ai.getHeadPortInst()); String n1 = segmentedNets.getNetName(ai.getTailPortInst()); int arcPImodels = SpiceSegmentedNets.getNumPISegments(res.doubleValue(), layoutTechnology.getMaxSeriesResistance()); if (arcPImodels > 1) { // have to break it up into smaller pieces double segCap = segmentedNets.getArcCap(ai)/(arcPImodels+1); double segRes = res.doubleValue()/arcPImodels; String segn0 = n0; String segn1 = n0; for (int i=0; i<arcPImodels; i++) { segn1 = n0 + "##" + i; // print cap on intermediate node if (i == (arcPImodels-1)) segn1 = n1; multiLinePrint(false, "R"+resCount+" "+segn0+" "+segn1+" "+TextUtils.formatDouble(segRes)+"\n"); resCount++; if (i < (arcPImodels-1)) { if (!segn1.equals("gnd") && segCap > layoutTechnology.getMinCapacitance()) { String capVal = TextUtils.formatDouble(segCap); if (!capVal.equals("0.00")) { multiLinePrint(false, "C"+capCount+" "+segn1+" 0 "+capVal+"fF\n"); capCount++; } } } segn0 = segn1; } } else { multiLinePrint(false, "R" + resCount + " " + n0 + " " + n1 + " " + TextUtils.formatDouble(res.doubleValue()) + "\n"); resCount++; } } } } // write out any directly-typed SPICE cards if (!useCDL) { boolean firstDecl = true; for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); if (ni.getProto() != Generic.tech().invisiblePinNode) continue; Variable cardVar = ni.getVar(SPICE_CARD_KEY); if (cardVar == null) continue; if (firstDecl) { firstDecl = false; multiLinePrint(true, "\n* Spice Code nodes in cell " + cell + "\n"); } emitEmbeddedSpice(cardVar, context, segmentedNets, info, false, forceEval); } } // finally, if this is the top level, // write out some very small resistors between any networks // that are asserted will be connected by the NCC annotation "exportsConnectedByParent" // this should really be done internally in the network tool with a switch, but // this hack works here for now NccCellAnnotations anna = NccCellAnnotations.getAnnotations(cell); if (cell == topCell && anna != null) { // each list contains all name patterns that are be shorted together if (anna.getExportsConnected().hasNext()) { multiLinePrint(true, "\n*** Exports shorted due to NCC annotation 'exportsConnectedByParent':\n"); } for (Iterator<List<NamePattern>> it = anna.getExportsConnected(); it.hasNext(); ) { List<NamePattern> list = it.next(); List<Network> netsToConnect = new ArrayList<Network>(); // each name pattern can match any number of exports in the cell for (NccCellAnnotations.NamePattern pat : list) { for (Iterator<PortProto> it3 = cell.getPorts
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -