BuildImpl:
CEDAR
PROGRAM
IMPORTS
CoreClasses, CoreFlat, CoreOps, CoreProperties, Flow, Globals, IO, Model, Real, RefTab, Rope, WriteCapa
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 ANY ← NIL, wireName: RefTab.Ref];
Units: PUBLIC REAL ← 2.0;
defaultIndex: NAT ← LAST[NAT];
Statistics gathered while reading in files.
totalNodes: INT ← 0;
totalFets: ARRAY[0..Model.maxFetTypes) OF INT ← ALL[0];
debug: PUBLIC BOOL ← FALSE;
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.ROPE ← NARROW[HashTable.Fetch[table: wireName, key: ct.public[ORD[CoreClasses.TransistorPort.gate]]].value];
ch1Name: Rope.ROPE ← NARROW[HashTable.Fetch[table: wireName, key: ct.public[ORD[CoreClasses.TransistorPort.ch1]]].value];
ch2Name: Rope.ROPE ← NARROW[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.ROPE ← NARROW[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: INT ← NARROW[CoreProperties.GetCellTypeProp[cell, CoreClasses.lengthProp], REF INT]^;
transistorWidth: INT ← NARROW[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.