BuildImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Ousterhout, February 7, 1984 1:22 pm
Barth, February 17, 1986 5:23:45 pm PST
Christian Le Cocq April 29, 1987 3:31:55 pm PDT
Bertrand Serlet October 18, 1986 9:00:17 pm PDT
This file contains the implementation of the package that builds up a circuit network.
DIRECTORY
Build,
Core,
CoreClasses,
CoreFlat,
CoreOps,
CoreProperties,
Flow,
Globals,
IO,
Model,
Real,
RefTab,
Rope,
WriteCapa;
BuildImpl: CEDAR PROGRAM
IMPORTS
CoreClasses, CoreFlat, CoreOps, CoreProperties, Flow, Globals, IO, Model, Real, RefTab, Rope, WriteCapa
EXPORTS
Build =
BEGIN OPEN Build;
CoreAttributes: TYPE = LIST OF CoreAttributeRec;
CoreAttributeRec: TYPE = RECORD [
port: CoreClasses.TransistorPort,
attribute: Rope.ROPE];
FlatCellProc: TYPE = PROC [cell: Core.CellType, target: CoreFlat.FlatCellTypeRec ← CoreFlat.allFlatCells, flatCell: CoreFlat.FlatCellTypeRec ← [], instance: CoreClasses.CellInstance ← NIL, parent: Core.CellType ← NIL, flatParent: CoreFlat.FlatCellTypeRec ← [], data: REF ANYNIL, wireName: RefTab.Ref];
Units: PUBLIC REAL ← 2.0;
defaultIndex: NATLAST[NAT];
Statistics gathered while reading in files.
totalNodes: INT ← 0;
totalFets: ARRAY[0..Model.maxFetTypes) OF INTALL[0];
debug: PUBLIC BOOLFALSE;
EOF: ERROR = CODE;
The following type is used to distinguish between terminals of a transistor.
Terminal: TYPE = {gate, source, drain};
BuildNode: PUBLIC PROC[flatWire: CoreFlat.FlatWire, globalVars: Globals.GlobalVars] RETURNS [Globals.Node] = {
This procedure sees if a given node exists, and makes a new one if necessary. A pointer is returned to the new or existing node.
new: Globals.Node;
val: RefTab.Val;
found: BOOLEAN;
Find the hash entry for the node, and then see if it contains a node pointer. If not, it is a new entry, so we have to create the node.
entry ← Hash.Find[globalVars.nodeTable, name];
IF entry.clientData # NIL THEN RETURN[NARROW[entry.clientData]];
[found: found, val: val] ← RefTab.Fetch[globalVars.nodeTable, flatWire];
IF found THEN RETURN[NARROW[val]];
new ← NEW[Globals.NodeRec];
new.name ← name;
new.flatWire ← flatWire;
new.cap ← WriteCapa.GetRootCap[flatWire.wire];
new.res ← 0;
new.hiTime ← -1.0;
new.loTime ← -1.0;
new.firstPointer ← NIL;
new.always0 ← FALSE;
new.always1 ← FALSE;
new.input ← FALSE;
new.precharged ← FALSE;
new.dynamic ← FALSE;
new.ratioError ← FALSE;
new.watched ← FALSE;
entry.clientData ← new;
[] ← RefTab.Store[x: globalVars.nodeTable, key: flatWire, val: new];
totalNodes ← totalNodes + 1;
RETURN [new];
};
BuildPointer: PROC[node: Globals.Node, fet: Globals.Fet] = {
This procedure links a transistor into the list of those that connect to a node.
p: Globals.Pointer ← NEW[Globals.PointerRec ← [fet, node.firstPointer]];
node.firstPointer ← p;
};
BuildFet: PROC[gate, source, drain: CoreFlat.FlatWire, fetData: REF ANY, l, w: REAL, globalVars: Globals.GlobalVars] RETURNS [fet: Globals.Fet] = {
This procedure builds a transistor data structure and links it into the network under construction. The fetData parameter must NARROW into the type of the transistor. A pointer to the new transistor is saved in lastFet.
cap: REAL;
f2: Globals.Fet;
ptr: Globals.Pointer;
type: REF INTEGER ← NARROW[fetData];
fet ← NEW[Globals.FetRec];
fet.type ← type^;
fet.gate ← BuildNode[gate, globalVars];
fet.source ← BuildNode[source, globalVars];
fet.drain ← BuildNode[drain, globalVars];
fet.area ← l*w;
fet.aspect ← l/w;
fet.x ← x;
fet.y ← y;
Detect loads and super buffers.
IF fet.type = Model.fetNDep THEN {
IF fet.source = globalVars.VddNode THEN {
IF fet.drain = fet.gate THEN fet.type ← Model.fetNLoad
ELSE fet.type ← Model.fetNSuper;
}
ELSE IF fet.drain = globalVars.VddNode THEN {
IF fet.source = fet.gate THEN fet.type ← Model.fetNLoad
ELSE fet.type ← Model.fetNSuper;
};
};
If the gate doesn't connect to either source or drain, then add its gate-channel capacitance onto the gate. Also add in source and drain overlap capacitance.
IF (fet.gate # fet.source) AND (fet.gate # fet.drain)
THEN fet.gate.cap ← fet.gate.cap + (fet.area * Model.TypeTable[fet.type].cPerArea);
cap ← w * Model.TypeTable[fet.type].cPerWidth;
IF (fet.gate # fet.drain) THEN {
fet.drain.cap ← fet.drain.cap + cap;
fet.gate.cap ← fet.gate.cap + cap;
};
IF fet.gate # fet.source THEN {
fet.source.cap ← fet.source.cap + cap;
fet.gate.cap ← fet.gate.cap + cap;
};
See if there is already a transistor of the same type connecting between the same three nodes. If so, just lump the two transistors into a single bigger transistor (this handles pads with multiple driving transistors in parallel). If not, then create pointers to the transistor from each of the nodes it connects to.
IF (fet.source # globalVars.VddNode) AND (fet.source # globalVars.GroundNode)
THEN ptr ← fet.source.firstPointer
ELSE IF (fet.drain # globalVars.VddNode) AND (fet.drain # globalVars.GroundNode)
THEN ptr ← fet.drain.firstPointer
ELSE IF (fet.gate # globalVars.VddNode) AND (fet.gate # globalVars.GroundNode)
THEN ptr ← fet.gate.firstPointer
ELSE ptr ← NIL;
WHILE ptr # NIL DO
f2 ← ptr.fet;
IF (f2.gate = fet.gate) AND (f2.drain = fet.drain)
AND (f2.source = fet.source) AND (f2.type = fet.type) THEN {
f2.area ← f2.area + fet.area;
f2.aspect ← 1.0/(1.0/f2.aspect + 1.0/fet.aspect);
fet ← f2;
EXIT;
};
ptr ← ptr.next;
ENDLOOP;
IF ptr = NIL THEN {
BuildPointer[fet.gate, fet];
IF fet.drain # fet.gate THEN BuildPointer[fet.drain, fet];
IF (fet.source # fet.gate) AND (fet.source # fet.drain)
THEN BuildPointer[fet.source, fet];
fet.on0 ← Model.TypeTable[fet.type].on0;
fet.on1 ← Model.TypeTable[fet.type].on1;
fet.onAlways ← Model.TypeTable[fet.type].onAlways;
};
totalFets[fet.type] ← totalFets[fet.type] + 1;
RETURN;
};
BuildAtt: PROC[fet: Globals.Fet, terminal: Terminal, string: Rope.ROPE] RETURNS [Rope.ROPE] = {
This procedure does all of the work of making a new attribute. It's called by separate procedures to handle Thyme and Sim formats. The string must be of the form "Cr:goo" or "Crystal:goo"; otherwise, this attribute is ignored (to make this work with Thyme, the colons are optional). The attribute may be used to set the transistor's type, modify its flags, or allocate flow control records. If all goes well, NIL is returned. Otherwise the return value is an error string.
pos, index: INT;
pos ← Rope.Run[string, 0, "Crystal:", 0, FALSE];
IF pos < 7 THEN {
pos ← Rope.Run[string, 0, "Cr:", 0, FALSE];
IF pos < 2 THEN RETURN[NIL];
};
string ← Rope.Replace[base: string, start: 0, len: pos, with: NIL];
If the attribute is attached to the transistor gate, it is the name of the transistor's type. Look up the name and set the type.
IF terminal = gate THEN {
index ← Model.NameToIndex[string];
IF index < 0 THEN
RETURN [IO.PutFR["unknown transistor type %s", IO.rope[string]]];
fet.type ← index;
RETURN [NIL];
};
If the attribute is "In" or "Out", then just set a flag in the transistor. Otherwise, create special flow control information.
IF Rope.Equal[string, "In", FALSE] OR Rope.Equal[string, "IN", FALSE] THEN {
IF terminal = source THEN fet.noDrainInfo ← TRUE
ELSE fet.noSourceInfo ← TRUE;
}
ELSE IF Rope.Equal[string, "Out", FALSE] OR Rope.Equal[string, "OUT", FALSE] THEN {
IF terminal = source THEN fet.noSourceInfo ← TRUE
ELSE fet.noDrainInfo ← TRUE;
}
ELSE Flow.Build[fet: fet, name: string, source: terminal=source];
RETURN [NIL];
};
Stats: PUBLIC PROC[] = {
total: INT ← 0;
IO.PutF[Globals.StdOut, "Total number of nodes: %d.\n", IO.int[totalNodes]];
FOR i:INT IN [0..Model.maxFetTypes) DO
IF totalFets[i] = 0 THEN LOOP;
IO.PutF[Globals.StdOut, "Number of %s transistors: %d.\n", IO.rope[Model.TypeTable[i].name], IO.int[totalFets[i]]];
total ← total + totalFets[i];
ENDLOOP;
IO.PutF[Globals.StdOut, "Total number of transistors: %d.\n", IO.int[total]];
};
InOutGuess: PUBLIC PROC [ct: Core.CellType, r1, r2, r3, r4, r5: Rope.ROPE, globalVars: Globals.GlobalVars] ~ {
GuessNode: PROC[wire: Core.Wire] = {
node: Globals.Node;
IF wire.size=0 THEN {
name: Rope.ROPE ← CoreOps.GetFullWireNames[rc.public, wire].first;
IF Rope.Equal[name, r1] THEN RETURN;
IF Rope.Equal[name, r2] THEN RETURN;
IF Rope.Equal[name, r3] THEN RETURN;
IF Rope.Equal[name, r4] THEN RETURN;
IF Rope.Equal[name, r5] THEN RETURN;
node ← BuildNode[name, globalVars];
IF node=globalVars.VddNode OR node=globalVars.GroundNode THEN RETURN;
node.input ← TRUE;
FOR iFet: Globals.Pointer ← node.firstPointer, iFet.next UNTIL iFet=NIL DO
IF iFet.fet.source= node OR iFet.fet.drain=node THEN {
node.input ← FALSE;
EXIT;
};
ENDLOOP;
IF node.input THEN IO.PutF[Globals.StdOut, "\ninput: %g", IO.rope[name]]
ELSE {
node.output ← TRUE;
IO.PutF[Globals.StdOut, "\noutput: %g", IO.rope[name]];
};
};
};
rc: Core.CellType;
FOR rc ← ct, CoreOps.Recast[rc] UNTIL rc.class.recast = NIL DO ENDLOOP;
CoreOps.VisitRootAtomics[root: rc.public, eachWire: GuessNode];
};
NetFromCore: PUBLIC PROC[ct: Core.CellType] RETURNS [globalVars: Globals.GlobalVars]= {
Flatten: PROC [name: Rope.ROPE, ct: Core.CellType] = {
IF Globals.Stop^ THEN RETURN;
IF ct.class=CoreClasses.transistorCellClass THEN {
gateName: Rope.ROPENARROW[HashTable.Fetch[table: wireName, key: ct.public[ORD[CoreClasses.TransistorPort.gate]]].value];
ch1Name: Rope.ROPENARROW[HashTable.Fetch[table: wireName, key: ct.public[ORD[CoreClasses.TransistorPort.ch1]]].value];
ch2Name: Rope.ROPENARROW[HashTable.Fetch[table: wireName, key: ct.public[ORD[CoreClasses.TransistorPort.ch2]]].value];
tran: CoreClasses.Transistor ← NARROW[ct.data];
fet: Globals.Fet ← NARROW[BuildFet[gateName, ch1Name, ch2Name, tranTab[tran.type], Real.Float[tran.length], Real.Float[tran.width], globalVars]];
atts: CoreAttributes ← NARROW[CoreProperties.GetCellTypeProp[from: ct, prop: crystalAttProp]];
FOR att: CoreAttributes ← atts, att.rest UNTIL att=NIL DO
error: Rope.ROPE ← BuildAtt[fet, (SELECT att.first.port FROM gate => gate, ch1 => source, ch2 => drain, ENDCASE => ERROR), Rope.Cat["Cry: ", att.first.attribute]];
IF error#NIL THEN IO.PutF[Globals.StdOut, "Attribute error: %s.\n", IO.rope[error]];
ENDLOOP;
IF debug THEN IO.PutF[Globals.StdOut, "\nFet: %g, %g, %g", IO.rope[gateName], IO.rope[ch1Name], IO.rope[ch2Name]];
}
ELSE {
MarkAtomicPublic: PROC[wire: Core.Wire] = {
[] ← HashTable.Store[table: publics, key: wire, value: $Public];
};
InsertInternal: PROC[wire: Core.Wire] = {
IF NOT HashTable.Fetch[table: publics, key: wire].found THEN {
thisWireName: Rope.ROPE ← Rope.Cat[name, "/", CoreOps.GetFullWireNames[rct.internal, wire].first];
InsertWire[wire, thisWireName];
};
};
BindPublicToActual: CoreOps.EachWirePairProc = {
PROC [actualWire, publicWire: Wire]
IF publicWire.size=0 THEN {
name: Rope.ROPENARROW[HashTable.Fetch[table: wireName, key: actualWire].value];
[] ← HashTable.Store[table: wireName, key: publicWire, value: name];
};
};
rc: Core.CellType;
rct: CoreClasses.RecordCellType;
publics: HashTable.Table ← HashTable.Create[];
FOR rc ← ct, CoreOps.Recast[rc] UNTIL rc.class.recast = NIL DO ENDLOOP;
rct ← NARROW[rc.data];
CoreOps.VisitRootAtomics[root: rc.public, eachWire: MarkAtomicPublic];
CoreOps.VisitRootAtomics[root: rct.internal, eachWire: InsertInternal];
FOR in: NAT IN [0..rct.size) DO
instanceName: Rope.ROPE ← CoreClasses.GetCellInstanceName[rct[in]];
IF instanceName=NIL THEN instanceName ← IO.PutFR["%g[%g]", IO.rope[CoreOps.GetCellTypeName[rct[in].type]], IO.int[in]];
IF CoreOps.VisitBinding[actual: rct[in].actual, public: rct[in].type.public, eachWirePair: BindPublicToActual] THEN ERROR;
Flatten[name: Rope.Cat[name, ".", instanceName], ct: rct[in].type];
ENDLOOP;
};
};
InsertPublic: CoreOps.EachWireProc = {
IF wire.size=0 THEN {
name: Rope.ROPE ← CoreOps.GetFullWireNames[rc.public, wire].first;
IO.PutF[Globals.StdOut, "\nExternal: %g", IO.rope[name]];
InsertWire[wire, name];
};
};
InsertWire: PROC [wire: Core.Wire, name: Rope.ROPE] = {
node: Globals.Node ← BuildNode[name, globalVars];
IF debug THEN IO.PutF[Globals.StdOut, "\nNode: %g", IO.rope[name]];
[] ← HashTable.Store[table: wireName, key: wire, value: name];
node.cap ← WriteCapa.GetRootCap[wire];
IF node.cap#0 THEN capNodes ← capNodes + 1;
};
entry: Hash.Entry;
tranTab: ARRAY CoreClasses.TransistorType OF REF INTEGER;
wireName: HashTable.Table ← HashTable.Create[];
rc: Core.CellType;
IO.PutF[Globals.StdOut, "\nConverting Core data structure for cell type %g . . .", IO.rope[CoreOps.GetCellTypeName[ct]]];
FOR rc ← ct, CoreOps.Recast[rc] UNTIL rc.class.recast = NIL DO ENDLOOP;
globalVars ← NEW[Globals.GlobalVarsRec];
globalVars.nodeTable ← Hash.NewTable[];
globalVars.GroundNode ← BuildNode["GND", globalVars];
globalVars.GroundNode.input ← TRUE;
globalVars.GroundNode.always0 ← TRUE;
entry ← Hash.Find[globalVars.nodeTable, "Gnd"];
entry.clientData ← globalVars.GroundNode;
globalVars.VddNode ← BuildNode["VDD", globalVars];
globalVars.VddNode.input ← TRUE;
globalVars.VddNode.always1 ← TRUE;
entry ← Hash.Find[globalVars.nodeTable, "Vdd"];
entry.clientData ← globalVars.VddNode;
FOR i: INTEGER IN [0..Model.maxFetTypes) DO
ttype: Model.TType ← Model.TypeTable[i];
IF ttype = NIL THEN EXIT;
SELECT TRUE FROM
Rope.Equal[ttype.name, "NETran"] => tranTab[nE] ← NEW[INTEGER ← i];
Rope.Equal[ttype.name, "PETran"] => tranTab[pE] ← NEW[INTEGER ← i];
Rope.Equal[ttype.name, "NDTran"] => tranTab[nD] ← NEW[INTEGER ← i];
ENDCASE;
ENDLOOP;
IF CoreOps.VisitWire[wire: rc.public, eachWire: InsertPublic] THEN ERROR;
Flatten[name: NIL, ct: ct];
IO.PutRope[Globals.StdOut, " \nFinished\n: "];
};
NodeFromRope: PUBLIC PROC [id: Rope.ROPE, globalVars: Globals.GlobalVars] RETURNS [node: Globals.Node] ~ {
flatWire: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec ← CoreFlat.ParseWirePath[globalVars.rootCell, id]];
node ← NARROW[RefTab.Fetch[globalVars.nodeTable, flatWire].val];
};
RopeFromNode: PUBLIC PROC [node: Globals.Node, globalVars: Globals.GlobalVars] RETURNS [id: Rope.ROPE] ~ {
id ← CoreFlat.WirePathRope[globalVars.rootCell, node.flatWire^];
};
NetFromCore: PUBLIC PROC[ct: Core.CellType] RETURNS [globalVars: Globals.GlobalVars] = {
InsertPublic: PROC[wire: Core.Wire] = {
InsertWire[wire, CoreFlat.rootCellType, globalVars, wireName, public];
};
wireName: RefTab.Ref ← RefTab.Create[];
IO.PutF[Globals.StdOut, "\nConverting cell %g . . .", IO.rope[CoreOps.GetCellTypeName[ct]]];
globalVars ← NEW[Globals.GlobalVarsRec];
globalVars.rootCell ← ct;
globalVars.nodeTable ← RefTab.Create[equal: CoreFlat.FlatWireEqual, hash: CoreFlat.FlatWireHash];
CoreOps.VisitRootAtomics[root: ct.public, eachWire: InsertPublic];
Flatten[cell: ct, data: globalVars, wireName: wireName];
globalVars.GroundNode ← NodeFromRope[GndName, globalVars];
globalVars.GroundNode.input ← TRUE;
globalVars.GroundNode.always0 ← TRUE;
globalVars.VddNode ← NodeFromRope[VddName, globalVars];
globalVars.VddNode.input ← TRUE;
globalVars.VddNode.always1 ← TRUE;
IO.PutRope[Globals.StdOut, " \nFinished\n"];
};
InsertWire: PROC [wire: Core.Wire, flatCell: CoreFlat.FlatCellTypeRec, globalVars: Globals.GlobalVars, wireName: RefTab.Ref, wireRoot: CoreFlat.WireRoot ← internal] = {
node: Globals.Node;
flatWire: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
flatWire.flatCell ← flatCell;
flatWire.wireRoot ← wireRoot;
flatWire.wire ← wire;
node ← BuildNode[flatWire, globalVars];
[] ← RefTab.Store[x: wireName, key: wire, val: flatWire];
};
Flatten: FlatCellProc = {
globalVars: Globals.GlobalVars ← NARROW[data];
SELECT cell.class FROM
CoreClasses.transistorCellClass => {
transistorLength: INTNARROW[CoreProperties.GetCellTypeProp[cell, CoreClasses.lengthProp], REF INT]^;
transistorWidth: INTNARROW[CoreProperties.GetCellTypeProp[cell, CoreClasses.widthProp], REF INT]^;
gateWire: CoreFlat.FlatWire ← NARROW[RefTab.Fetch[x: wireName, key: cell.public[ORD[CoreClasses.TransistorPort.gate]]].val];
ch1Wire: CoreFlat.FlatWire ← NARROW[RefTab.Fetch[x: wireName, key: cell.public[ORD[CoreClasses.TransistorPort.ch1]]].val];
ch2Wire: CoreFlat.FlatWire ← NARROW[RefTab.Fetch[x: wireName, key: cell.public[ORD[CoreClasses.TransistorPort.ch2]]].val];
tran: CoreClasses.Transistor ← NARROW[cell.data];
fet: Globals.Fet ← BuildFet[gateWire, ch1Wire, ch2Wire, tranTab[tran.type], Real.Float[transistorLength], Real.Float[transistorWidth], globalVars];
atts: CoreAttributes ← NARROW[CoreProperties.GetCellTypeProp[from: cell, prop: crystalAttProp]];
FOR att: CoreAttributes ← atts, att.rest UNTIL att=NIL DO
error: Rope.ROPE ← BuildAtt[fet, (SELECT att.first.port FROM gate => gate, ch1 => source, ch2 => drain, ENDCASE => ERROR), Rope.Cat["Cry: ", att.first.attribute]];
IF error#NIL THEN IO.PutF[Globals.StdOut, "Attribute error: %s.\n", IO.rope[error]];
ENDLOOP;
};
CoreClasses.recordCellClass => {
MarkAtomicPublic: PROC[wire: Core.Wire] = {
[] ← RefTab.Store[x: publics, key: wire, val: $Public];
};
InsertInternal: PROC[wire: Core.Wire] = {
IF NOT RefTab.Fetch[x: publics, key: wire].found THEN {
InsertWire[wire, flatCell, globalVars, wireName];
};
};
rct: CoreClasses.RecordCellType;
publics: RefTab.Ref ← RefTab.Create[cell.public.size+1];
rct ← NARROW[cell.data];
CoreOps.VisitRootAtomics[root: cell.public, eachWire: MarkAtomicPublic];
CoreOps.VisitRootAtomics[root: rct.internal, eachWire: InsertInternal];
NextCellType[cell, target, flatCell, instance, parent, flatParent, data, wireName, Flatten]
};
ENDCASE => {
NextCellType[cell, target, flatCell, instance, parent, flatParent, data, wireName, Flatten]
};
};
NextCellType: PROC [cell: Core.CellType, target: CoreFlat.FlatCellTypeRec, flatCell: CoreFlat.FlatCellTypeRec, instance: CoreClasses.CellInstance, parent: Core.CellType, flatParent: CoreFlat.FlatCellTypeRec, data: REF ANY, wireName: RefTab.Ref, proc: FlatCellProc] = {
BindCellType: CoreFlat.UnboundFlatCellProc = {
BindPublicToActual: CoreOps.EachWirePairProc = {
PROC [actualWire, publicWire: Wire]
IF publicWire.size=0 THEN {
flatWire: CoreFlat.FlatWire ← NARROW[RefTab.Fetch[x: wireName, key: actualWire].val];
[] ← RefTab.Store[x: wireName, key: publicWire, val: flatWire];
};
};
IF CoreOps.VisitBinding[actual: IF instance=NIL OR instance.type#cell THEN previous.public ELSE instance.actual, public: cell.public, eachWirePair: BindPublicToActual] THEN ERROR;
proc[cell, target, flatCell, instance, parent, flatParent, data, wireName];
};
previous: Core.CellType ← cell;
CoreFlat.NextUnboundCellType[cell, target, flatCell, instance, defaultIndex, parent, flatParent, data, BindCellType];
};
SetAtt: PUBLIC PROC [transistor: Core.CellType, port: CoreClasses.TransistorPort, attribute: Core.ROPE] RETURNS [sameTransistor: Core.CellType] = {
currentAtts: CoreAttributes ← NARROW[CoreProperties.GetCellTypeProp[from: transistor, prop: crystalAttProp]];
IF transistor.class#CoreClasses.transistorCellClass THEN ERROR;
FOR att: CoreAttributes ← currentAtts, att.rest UNTIL att=NIL DO
IF att.first.port=port THEN {
att.first.attribute ← attribute;
EXIT;
};
REPEAT FINISHED => {
currentAtts ← CONS[[port, attribute], currentAtts];
CoreProperties.PutCellTypeProp[on: transistor, prop: crystalAttProp, value: currentAtts];
};
ENDLOOP;
sameTransistor ← transistor;
};
GndName: Rope.ROPE ← "public.Gnd";
VddName: Rope.ROPE ← "public.Vdd";
crystalAttProp: ATOM = CoreProperties.RegisterProperty[prop: $CrystalAtt];
tranTab: ARRAY CoreClasses.TransistorType OF REF INTEGER;
FOR i: INTEGER IN [0..Model.maxFetTypes) DO
ttype: Model.TType ← Model.TypeTable[i];
IF ttype = NIL THEN EXIT;
SELECT TRUE FROM
Rope.Equal[ttype.name, "NETran"] => tranTab[nE] ← NEW[INTEGER ← i];
Rope.Equal[ttype.name, "PETran"] => tranTab[pE] ← NEW[INTEGER ← i];
Rope.Equal[ttype.name, "NDTran"] => tranTab[nD] ← NEW[INTEGER ← i];
ENDCASE;
ENDLOOP;
END.