BuildImpl.mesa
Ousterhout, February 7, 1984 1:22 pm
Barth, February 17, 1986 5:23:45 pm PST
This file contains the implementation of the package that builds up a circuit network by reading from information from a .sim or .thy format file.
DIRECTORY
Build, Core, CoreClasses, CoreOps, CoreProperties, CrystalOps, Flow, FS, Globals, Hash, HashTable, IO, Model, Parse, Real, Rope, ThymeParser;
BuildImpl: CEDAR PROGRAM
IMPORTS CoreClasses, CoreOps, CoreProperties, Flow, FS, Globals, Hash, HashTable, IO, Model, Parse, Real, Rope, ThymeParser
EXPORTS Build, CrystalOps =
BEGIN OPEN Globals;
Units: PUBLIC REAL ← 2.0;
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};
Quad: PROC[area, perim: REAL] RETURNS [aspect: REAL] = {
This procedure returns the length/width (aspect ratio) of a piece of material, given its area and perimeter. This is done by assuming that the material is rectangular in shape and then solving a quadratic equation. The assumption of rectangularity produces good results as long as the material is of uniform width and has no branches. Otherwise, it may overestimate the aspect ratio. Note: the value returned is always greater tha or equal to 1.
root: REAL;
root ← Real.SqRt[(perim*perim) - (16*area)];
aspect ← (perim + root)/(perim - root);
RETURN [aspect];
};
BuildNode: PUBLIC PROC[name: Rope.ROPE] RETURNS [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: Node;
entry: Hash.Entry;
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[NodeTable, name];
IF entry.clientData # NIL
THEN RETURN[NARROW[entry.clientData]];
new ← NEW[NodeRec];
new.name ← name;
new.cap ← 0;
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;
totalNodes ← totalNodes + 1;
RETURN [new];
};
BuildPointer: PROC[node: Node, fet: Fet] = {
This procedure links a transistor into the list of those that connect to a node.
p: Pointer ← NEW[PointerRec ← [fet, node.firstPointer]];
node.firstPointer ← p;
};
BuildFet: ThymeParser.TransistorProc = {
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;
fet, f2: Fet;
ptr: Pointer;
type: REF INTEGER ← NARROW[fetData];
fet ← NEW[FetRec];
fet.type ← type^;
fet.gate ← BuildNode[gate];
fet.source ← BuildNode[source];
fet.drain ← BuildNode[drain];
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 = VddNode THEN {
IF fet.drain = fet.gate THEN fet.type ← Model.fetNLoad
ELSE fet.type ← Model.fetNSuper;
}
ELSE IF fet.drain = 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 # VddNode) AND (fet.source # GroundNode)
THEN ptr ← fet.source.firstPointer
ELSE IF (fet.drain # VddNode) AND (fet.drain # GroundNode)
THEN ptr ← fet.drain.firstPointer
ELSE IF (fet.gate # VddNode) AND (fet.gate # 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 [fet];
};
BuildAtt: PROC[fet: 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];
};
SimAtt: PROC[fet: Fet, string: Rope.ROPE] RETURNS [Rope.ROPE] = {
This local procedure breaks up an attribute string into its component attributes, and processes them. This routine handles only transistor attributes. The attribute string has the form "x=a,b,c..." where x is "s", "d", or "g" to select the transistor terminal to which the attribute applies. Each of the comma-separated strings a, b, etc. is of the form "Crystal:goo" or "Cr:goo", where "goo" is the attribute information we store. Attributes not starting out with "Crystal" or "Cr" are ignored. The return result is NIL if everything went OK, else it contains a rope describing the attribute. If the attribute is parsed correctly, flow control records may be allocated, and/or the transistor's flags may be set.
terminal: Terminal;
att, err: Rope.ROPE;
length: INT;
Make sure that the attribute has the "x=" stuff at the front.
IF Rope.Length[string] < 3 THEN RETURN ["bad format"];
SELECT Rope.Fetch[string, 0] FROM
's => terminal ← source;
'd => terminal ← drain;
'g => terminal ← gate;
ENDCASE => RETURN ["bad terminal"];
IF Rope.Fetch[string, 1] # '= THEN RETURN ["bad format"];
Now skim off the comma-separated attributes one at a time.
length ← 2;
WHILE TRUE DO
string ← Rope.Replace[base: string, start: 0, len: length, with: NIL];
IF Rope.Length[string] = 0 THEN EXIT;
length ← Rope.Find[s1: string, s2: ",", pos1: 0, case: FALSE];
IF length < 0 THEN length ← Rope.Length[string];
att ← Rope.Substr[string, 0, length];
length ← length + 1;
err ← BuildAtt[fet, terminal, att];
IF err # NIL THEN RETURN[err];
ENDLOOP;
RETURN [NIL];
};
ThymeAtt: ThymeParser.AttributeProc = {
Called by the Thyme-file parser for each attribute found. Just pass the attribute along to BuildAtt.
err: Rope.ROPENIL;
fet: Fet ← NARROW[fetHandle];
IF Rope.Equal[name, "Source"] THEN err ← BuildAtt[fet, source, value]
ELSE IF Rope.Equal[name, "Drain"] THEN err ← BuildAtt[fet, drain, value]
ELSE IF Rope.Equal[name, "Gate"] THEN err ← BuildAtt[fet, gate, value];
IF err # NIL THEN IO.PutF[StdOut, "Attribute error: %s.\n", IO.rope[err]];
};
GetRope: PROC[stream: IO.STREAM] RETURNS [Rope.ROPE] = {
A utility procedure that returns the next token from the stream. The token consists of any characters between white spaces, and is returned as a rope.
rope: Rope.ROPE;
[token: rope] ← IO.GetTokenRope[stream, Parse.WhiteSpace];
RETURN [rope];
};
NetFromSim: PUBLIC PROC[file: Rope.ROPE] RETURNS[error: Rope.ROPE] = {
lineIndex: INT ← 0;
keyChar: CHAR;
fileStream, lineStream: IO.STREAM;
msg: Rope.ROPENIL;
rope, lineRope: Rope.ROPE;
entry: Hash.Entry;
fileStream ← FS.StreamOpen[file
! FS.Error => IF error.group # bug
THEN {msg ← error.explanation ; CONTINUE}];
IF msg # NIL THEN RETURN [msg];
GroundNode ← BuildNode["GND"];
GroundNode.input ← TRUE;
GroundNode.always0 ← TRUE;
entry ← Hash.Find[NodeTable, "Gnd"];
entry.clientData ← GroundNode;
VddNode ← BuildNode["VDD"];
VddNode.input ← TRUE;
VddNode.always1 ← TRUE;
entry ← Hash.Find[NodeTable, "Vdd"];
entry.clientData ← VddNode;
Process the file one line at a time. The first character of each line determines the significance of the line.
WHILE NOT IO.EndOf[fileStream] DO
ENABLE {
EOF => {
dummy: INT;
dummy ← 22;
error ← IO.PutFR["Not enough info on .sim file line near byte %d.\n", IO.int[lineIndex]];
CONTINUE;
};
IO.EndOfStream => {
dummy: INT;
dummy ← 22;
error ← IO.PutFR["Not enough info on .sim file line near byte %d.\n", IO.int[lineIndex]];
CONTINUE;
};
ANY => {
dummy: INT;
dummy ← 22;
error ← IO.PutFR["Unknown error in .sim file on line near byte %d.\n", IO.int[lineIndex]];
CONTINUE;
};
};
IF Stop^ OR (error # NIL) THEN RETURN [error];
IF lineRope # NIL
THEN lineIndex ← lineIndex + Rope.Length[lineRope] + 1;
lineRope ← IO.GetLineRope[fileStream];
IF Rope.Length[lineRope] = 0 THEN LOOP;
lineStream ← IO.RIS[lineRope, lineStream];
keyChar ← IO.GetChar[lineStream];
SELECT keyChar FROM
'|' indicates header information with technology and other junk. All that's used right now is the unit scale factor. Line syntax: "| units: %f"
'| => {
rope ← GetRope[lineStream];
Units ← IO.GetReal[lineStream];
Units ← Units/100.0;
};
'e' indicates an NMOS enhancement transistor, 'd' indicates an NMOS depletion transistor. In either case, the format of the line is "e gate source drain length width xloc yloc"
'e, 'd, 'p, 'n => {
length, width, xloc, yloc: REAL;
gate, source, drain: Rope.ROPE;
fet: Fet;
err: Rope.ROPE;
type: REF INTEGER ← NEW[INTEGER];
Parse up the input line, and create the transistor.
IF keyChar = 'e THEN type^ ← Model.fetNEnh
ELSE IF keyChar = 'd THEN type^ ← Model.fetNDep
ELSE IF keyChar = 'n THEN type^ ← Model.fetNChan
ELSE type^ ← Model.fetPChan;
gate ← GetRope[lineStream];
source ← GetRope[lineStream];
drain ← GetRope[lineStream];
length ← IO.GetReal[lineStream];
length ← length*Units;
width ← IO.GetReal[lineStream];
width ← width*Units;
xloc ← IO.GetReal[lineStream];
xloc ← xloc*Units;
yloc ← IO.GetReal[lineStream];
yloc ← yloc*Units;
fet ← NARROW[BuildFet[gate, source, drain, type, length, width, xloc, yloc], Fet];
Handle any attributes that are present.
WHILE TRUE DO
ENABLE IO.EndOfStream => EXIT;
err ← SimAtt[fet, GetRope[lineStream]];
IF err # NIL THEN RETURN [IO.PutFR["Attribute error near byte %d: %s.\n", IO.int[lineIndex], IO.rope[err]]];
ENDLOOP;
};
'N' means node area/perimeter information. Use it to compute node capacitance and resistance. Line format is: "N node diffArea diffPerim polyArea polyPerim metalArea metalPerim"
'N => {
node: Node;
diffArea, diffPerim, polyArea, polyPerim: REAL;
metalArea, metalPerim: REAL;
node ← BuildNode[GetRope[lineStream]];
diffArea ← IO.GetReal[lineStream];
diffArea ← diffArea*Units*Units;
diffPerim ← IO.GetReal[lineStream];
diffPerim ← diffPerim*Units;
polyArea ← IO.GetReal[lineStream];
polyArea ← polyArea*Units*Units;
polyPerim ← IO.GetReal[lineStream];
polyPerim ← polyPerim*Units;
metalArea ← IO.GetReal[lineStream];
metalArea ← metalArea*Units*Units;
metalPerim ← IO.GetReal[lineStream];
metalPerim ← metalPerim*Units;
IF diffArea # 0 THEN {
node.cap ← node.cap + (diffArea * Model.diffCPerArea^) + (diffPerim * Model.diffCPerPerim^);
node.res ← node.res + Model.diffR^ * Quad[area: diffArea, perim: diffPerim];
};
IF polyArea # 0 THEN {
node.cap ← node.cap + (polyArea * Model.polyCPerArea^);
node.res ← node.res + Model.polyR^ * Quad[area: polyArea, perim: polyPerim];
};
IF metalArea # 0 THEN {
node.cap ← node.cap + (metalArea * Model.metalCPerArea^);
node.res ← node.res + Model.metalR^ * Quad[area: metalArea, perim: metalPerim];
};
};
'C' means capacitance information (a given .sim file should have either C lines or N lines but not both). Line syntax is: "C node1 node2 capacitance(ffs)"
'C => {
node1, node2: Node;
cap: REAL;
node1 ← BuildNode[GetRope[lineStream]];
node2 ← BuildNode[GetRope[lineStream]];
cap ← IO.GetReal[lineStream];
cap ← cap/1000.0;
node1.cap ← node1.cap + cap;
node2.cap ← node2.cap + cap;
};
ENDCASE;
ENDLOOP;
IO.Close[fileStream];
Make sure that there were actually Vdd and GND nodes present in the file.
IF VddNode.firstPointer = NIL THEN RETURN ["No Vdd node.\n"];
IF GroundNode.firstPointer = NIL THEN RETURN ["No GND node.\n"];
RETURN [NIL];
};
Stats: PUBLIC PROC[] = {
total: INT ← 0;
IO.PutF[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[StdOut, "Number of %s transistors: %d.\n", IO.rope[Model.TypeTable[i].name], IO.int[totalFets[i]]];
total ← total + totalFets[i];
ENDLOOP;
IO.PutF[StdOut, "Total number of transistors: %d.\n", IO.int[total]];
};
BuildCap: ThymeParser.CapacitorProc = {
This procedure is invoked once for each capacitor in a Thyme circuit description. It addes the given capacitance value to each of the capacitor's two terminals.
node1, node2: Node;
node1 ← BuildNode[nodeA];
node2 ← BuildNode[nodeB];
node1.cap ← node1.cap + pfs;
node2.cap ← node2.cap + pfs;
};
resistorErrors: INT ← 0;
BuildRes: ThymeParser.ResistorProc = {
This procedure is invoked once for each resistor in a Thyme circuit description. Right now, it just prints out an error message.
IF resistorErrors > 10 THEN RETURN;
IO.PutF[StdOut, "Crystal can't deal with explicit resistors; ignoring.\n"];
resistorErrors ← resistorErrors + 1;
IF resistorErrors > 10
THEN IO.PutF[StdOut, "No more messages of this kind will be printed...\n"];
};
NetFromThyme: PUBLIC PROC[file: Rope.ROPE] = {
entry: Hash.Entry;
table: ThymeParser.TypeList ← NIL;
refInteger: REF INTEGERNIL;
ttype: Model.TType;
msg: Rope.ROPENIL;
fileStream: IO.STREAM;
Set up the Vdd and Ground nodes.
GroundNode ← BuildNode["GND"];
GroundNode.input ← TRUE;
GroundNode.always0 ← TRUE;
entry ← Hash.Find[NodeTable, "Gnd"];
entry.clientData ← GroundNode;
VddNode ← BuildNode["VDD"];
VddNode.input ← TRUE;
VddNode.always1 ← TRUE;
entry ← Hash.Find[NodeTable, "Vdd"];
entry.clientData ← VddNode;
Build the table that describes the transistors Crystal understands.
FOR i: INTEGER IN [0..Model.maxFetTypes) DO
ttype ← Model.TypeTable[i];
IF ttype = NIL THEN EXIT;
refInteger ← NEW[INTEGER];
refInteger^ ← i;
table ← NEW[ThymeParser.TypeListRec ← [ttype.name, refInteger, table]];
ENDLOOP;
Read in the Thyme file.
fileStream ← FS.StreamOpen[file
! FS.Error => IF error.group # bug
THEN {msg ← error.explanation ; CONTINUE}];
IF msg # NIL THEN {
IO.PutF[StdOut, "%s\n Couldn't read Thyme file %s.\n", IO.rope[msg], IO.rope[file]];
RETURN;
};
[] ← ThymeParser.ReadFile[fileStream, StdOut, table, BuildRes, BuildCap, BuildFet, ThymeAtt, NIL, NIL, Stop];
};
NetFromCore: PUBLIC PROC[ct: Core.CellType] = {
Flatten: PROC [name: Rope.ROPE, ct: Core.CellType] = {
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: Fet ← NARROW[BuildFet[gateName, ch1Name, ch2Name, tranTab[tran.type], Real.Float[tran.length], Real.Float[tran.width], 0.0, 0.0]];
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[StdOut, "Attribute error: %s.\n", IO.rope[error]];
ENDLOOP;
IF debug THEN IO.PutF[StdOut, "\nFet: %g, %g, %g", IO.rope[gateName], IO.rope[ch1Name], IO.rope[ch2Name]];
}
ELSE {
MarkAtomicPublic: CoreOps.EachWireProc = {
IF wire.size=0 THEN [] ← HashTable.Store[table: publics, key: wire, value: $Public];
};
InsertInternal: CoreOps.EachWireProc = {
IF wire.size=0 AND NOT HashTable.Fetch[table: publics, key: wire].found THEN {
thisWireName: Rope.ROPE ← Rope.Cat[name, "/", CoreOps.GetFullWireNames[wire].first];
InsertWire[wire, thisWireName];
};
};
BindPublicToActual: CoreOps.EachWirePairProc = {
IF publicWire.size=0 THEN [] ← HashTable.Store[table: wireName, key: publicWire, value: HashTable.Fetch[table: wireName, key: actualWire].value];
};
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];
IF CoreOps.VisitWire[wire: rc.public, eachWire: MarkAtomicPublic] THEN ERROR;
IF CoreOps.VisitWire[wire: rct.internal, eachWire: InsertInternal] THEN ERROR;
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 InsertWire[wire, CoreOps.GetFullWireNames[wire].first];
};
InsertWire: PROC [wire: Core.Wire, name: Rope.ROPE] = {
node: Node ← BuildNode[name];
capRef: REF CrystalOps.pfs ← NARROW[CoreProperties.GetWireProp[from: wire, prop: crystalCapProp]];
IF debug THEN IO.PutF[StdOut, "\nNode: %g", IO.rope[name]];
[] ← HashTable.Store[table: wireName, key: wire, value: name];
node.cap ← IF capRef=NIL THEN 0.0 ELSE capRef^;
};
entry: Hash.Entry;
tranTab: ARRAY CoreClasses.TransistorType OF REF INTEGER;
wireName: HashTable.Table ← HashTable.Create[];
rc: Core.CellType;
IO.PutF[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;
GroundNode ← BuildNode["GND"];
GroundNode.input ← TRUE;
GroundNode.always0 ← TRUE;
entry ← Hash.Find[NodeTable, "Gnd"];
entry.clientData ← GroundNode;
VddNode ← BuildNode["VDD"];
VddNode.input ← TRUE;
VddNode.always1 ← TRUE;
entry ← Hash.Find[NodeTable, "Vdd"];
entry.clientData ← 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[StdOut, " Finished\n: "];
};
SetCap: PUBLIC PROC [wire: Core.Wire, cap: CrystalOps.pfs] RETURNS [sameWire: Core.Wire] = {
CoreProperties.PutWireProp[on: wire, prop: crystalCapProp, value: NEW[CrystalOps.pfs ← cap]];
sameWire ← wire;
};
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;
};
crystalCapProp: ATOM = CoreProperties.RegisterProperty[prop: $CrystalCap];
crystalAttProp: ATOM = CoreProperties.RegisterProperty[prop: $CrystalAtt];
CoreAttributes: TYPE = LIST OF CoreAttributeRec;
CoreAttributeRec: TYPE = RECORD [
port: CoreClasses.TransistorPort,
attribute: Rope.ROPE];
END.