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; totalNodes: INT _ 0; totalFets: ARRAY[0..Model.maxFetTypes) OF INT _ ALL[0]; debug: PUBLIC BOOL _ FALSE; EOF: ERROR = CODE; Terminal: TYPE = {gate, source, drain}; Quad: PROC[area, perim: REAL] RETURNS [aspect: REAL] = { root: REAL; root _ Real.SqRt[(perim*perim) - (16*area)]; aspect _ (perim + root)/(perim - root); RETURN [aspect]; }; BuildNode: PUBLIC PROC[name: Rope.ROPE] RETURNS [Node] = { new: Node; entry: Hash.Entry; 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] = { p: Pointer _ NEW[PointerRec _ [fet, node.firstPointer]]; node.firstPointer _ p; }; BuildFet: ThymeParser.TransistorProc = { 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; 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 (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; }; 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] = { 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 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 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] = { terminal: Terminal; att, err: Rope.ROPE; length: INT; 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"]; 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 = { err: Rope.ROPE _ NIL; 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] = { 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.ROPE _ NIL; 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; 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 '| => { rope _ GetRope[lineStream]; Units _ IO.GetReal[lineStream]; Units _ Units/100.0; }; 'e, 'd, 'p, 'n => { length, width, xloc, yloc: REAL; gate, source, drain: Rope.ROPE; fet: Fet; err: Rope.ROPE; type: REF INTEGER _ NEW[INTEGER]; 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]; 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 => { 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 => { 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]; 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 = { 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 = { 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 INTEGER _ NIL; ttype: Model.TType; msg: Rope.ROPE _ NIL; fileStream: IO.STREAM; 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.TypeTable[i]; IF ttype = NIL THEN EXIT; refInteger _ NEW[INTEGER]; refInteger^ _ i; table _ NEW[ThymeParser.TypeListRec _ [ttype.name, refInteger, table]]; ENDLOOP; 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.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: 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. ψ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. Statistics gathered while reading in files. The following type is used to distinguish between terminals of a transistor. 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. 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. 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. This procedure links a transistor into the list of those that connect to a node. 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. Detect loads and super buffers. 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. 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. 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. 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 the attribute is "In" or "Out", then just set a flag in the transistor. Otherwise, create special flow control information. 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. Make sure that the attribute has the "x=" stuff at the front. Now skim off the comma-separated attributes one at a time. Called by the Thyme-file parser for each attribute found. Just pass the attribute along to BuildAtt. 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. Process the file one line at a time. The first character of each line determines the significance of the line. '|' indicates header information with technology and other junk. All that's used right now is the unit scale factor. Line syntax: "| units: %f" '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" Parse up the input line, and create the transistor. Handle any attributes that are present. '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" '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)" Make sure that there were actually Vdd and GND nodes present in the file. 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. This procedure is invoked once for each resistor in a Thyme circuit description. Right now, it just prints out an error message. Set up the Vdd and Ground nodes. Build the table that describes the transistors Crystal understands. Read in the Thyme file. Κd˜šœ™Jšœ$™$Icode™'J˜Jšœ’™’J˜—J™šΟk ˜ JšœEœœ(˜—J˜šΟn œœ˜Jšœ-œœ'˜{Jšœ˜Jšœœ ˜J˜Jšœœœ˜J˜Jšœ,™,Jšœ œ˜Jš œ œœœœ˜7Jšœœœœ˜Jšœœœ˜J™MJšœ œ˜'J˜š žœœœœ œ˜8JšœΓ™ΓJ˜Jšœœ˜ J˜,J˜'Jšœ ˜J˜—J˜š ž œœœ œœ ˜:Jšœ‡™‡J˜ J˜Jšœˆ™ˆJ˜J˜#šœ˜Jšœœœ˜&—Jšœœ ˜J˜J˜ J˜ J˜J˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜J˜Jšœ˜ J˜—J˜šž œœ˜,JšœP™PJ˜Jšœ œ(˜8J˜J˜—Jšœ˜šΟbœ ˜(J™έJ™Jšœœ˜ J˜ J˜ J˜$J˜Jšœœ ˜Jšœ˜J˜J˜J˜J˜J˜J˜ J˜ J˜Jšœ™J˜šœœ˜"šœœ˜Jšœœ˜6Jšœ˜ J˜—šœœœ˜"Jšœœ˜7Jšœ˜ J˜—J˜—J˜Jšœž™žJ˜šœœ˜5JšœO˜S—J˜.šœœ˜ J˜$J˜"J˜—šœœ˜J˜&J˜"J˜—J˜JšœΎ™ΎJ˜šœœ˜7Jšœ˜"—šœœœ˜:Jšœ˜!—šœœœ˜8Jšœ˜ —Jšœœ˜šœœ˜J˜ š œœœœœ˜oJ˜J˜1J˜ Jšœ˜J˜—J˜Jšœ˜—šœœœ˜J˜Jšœœ˜:šœœ˜7Jšœ˜#—J˜(J˜(J˜2J˜—J˜J˜.Jšœ˜ J˜J˜—Jšœ˜š Πbnœœ,œœœ˜WJ™ήJ™Jšœ œ˜Jšœ)œ˜0šœ œ˜Jšœ$œ˜+Jšœ œœœ˜J˜—Jšœ>œ ˜LJšœŠ™Ššœœ˜J˜"šœ ˜Jšœœ%œ˜A—J˜Jšœœ˜ J˜—J˜Jšœ™J˜š œœœœœ˜LJš œœœœœ˜NJšœ˜—š œœœœœœ˜SJš œœœœœ˜NJšœ˜—Jšœ=˜AJ˜Jšœœ˜ J˜J˜—Jšœ˜š žœœœœœ˜AJšœΞ™ΞJ˜Jšœ˜Jšœœ˜Jšœœ˜ J˜JšœB™BJšœœœ˜6šœ˜!Jšœ˜Jšœ˜Jšœ˜Jšœœ˜#—Jšœœœ˜=Jšœ>™>J˜ šœœ˜ JšœAœ˜FJšœœœ˜%Jšœ7œ˜>Jšœ œ˜0J˜%J˜J˜#Jšœœœœ˜Jšœ˜—Jšœœ˜ Jšœ˜—Jšœ˜šŸœ˜'J™eJ˜Jšœ œœ˜Jšœ œ ˜J˜Jšœœ#˜EJšœœœ"˜HJšœœœ"˜GJ˜J˜JJ˜—Jšœ˜š žœœ œœœœ˜8Jšœ—™—J˜Jšœ œ˜J˜Jšœœ(˜:Jšœ˜J˜—J˜š ž œœœ œœ œ˜GJšœ œ˜Jšœ œ˜Jšœœœ˜"Jšœ œœ˜Jšœœ˜J˜šœ œ˜Jš œœ œœœ˜N—Jšœœœœ ˜$J˜Jšœœ˜Jšœœ˜J˜$J˜J˜Jšœœ˜Jšœœ˜J˜$J˜J˜Jšœo™oJ˜šœœœ˜!šœ˜šœ˜Jšœœ˜ J˜ Jšœœ<œ˜YJšœ˜ J˜—J˜šœ˜Jšœœ˜ J˜ Jšœœ<œ˜YJšœ˜ J˜—J˜šœ˜Jšœœ˜ J˜ Jšœœ=œ˜ZJšœ˜ J˜—J˜—J˜Jš œœ œœœ ˜.šœ ˜Jšœ3˜7—Jšœ œ˜&Jšœœœ˜'Jšœ œœ˜*Jšœ œ˜!šœ ˜J˜Jšœ™J˜˜J˜Jšœœ˜J˜Jšœ˜—J˜Jšœ±™±J˜˜Jšœœ˜ J˜J˜ Jšœ œ˜J˜!J˜Jšœ3™3J˜Jšœœ˜*Jšœœœ˜/Jšœœœ˜0Jšœ˜J˜J˜J˜Jšœ œ˜ J˜Jšœœ˜J˜Jšœœ˜J˜Jšœœ˜J˜J˜JšœœF˜RJ˜Jšœ'™'J˜šœœ˜ Jšœœœ˜J˜'Jšœœœœœ.œœ ˜lJšœ˜—J˜J˜—Jšœ³™³J˜˜J˜ Jšœ*œ˜/Jšœœ˜.J˜&Jšœ œ˜"J˜ Jšœ œ˜#J˜Jšœ œ˜"J˜ Jšœ œ˜#J˜Jšœ œ˜#J˜"Jšœœ˜%J˜0šœœ˜J˜\J˜MJ˜—šœœ˜J˜7J˜LJ˜—šœœ˜J˜9J˜OJ˜—J˜J˜—Jšœ›™›J˜˜J˜Jšœœ˜ J˜J˜'J˜'Jšœœ˜J˜J˜J˜J˜—Jšœ˜—Jšœ˜—J˜Jšœ˜J˜JšœI™IJ˜Jšœœœœ˜=Jšœœœœ˜@Jšœœ˜ Jšœ˜—J˜šžœœœ˜Jšœœ˜J˜Jšœ.œ˜Dšœœœ˜&Jšœœœ˜Jšœ1œ œ˜kJ˜Jšœ˜—Jšœ4œ ˜EJ˜—J˜šŸœ˜'J™‘J˜J˜J˜J˜J˜J˜J˜—J˜Jšœœ˜šŸœ˜&J™J˜Jšœœœ˜#JšœJ˜LJ˜$šœ˜JšœœD˜K—J˜—J˜J˜šž œœœ œ˜.J˜J˜Jšœœ˜"Jšœ œœœ˜J˜Jšœ œœ˜Jšœ œœ˜J˜J™ J˜J˜Jšœœ˜Jšœœ˜J˜$J˜J˜Jšœœ˜Jšœœ˜J˜$J˜J˜J™CJ˜šœœœ˜+J˜Jšœ œœœ˜Jšœ œœ˜J˜Jšœœ<˜GJšœ˜—J˜J™J˜šœ œ˜Jš œœ œœœ˜N—šœœœ˜Jšœ7œ œ ˜VJšœ˜J˜—Jšœ˜Jšœ]œœ˜mJ˜J˜J˜—šž œœœ˜/J˜šžœœ œ˜6šœ*œ˜2Jšœœœ1œ+˜{Jšœœœ1œ*˜yJšœœœ1œ*˜yJšœœ ˜/Kšœ œv˜‡KšœœA˜^šœ&œœ˜9Kš œ œœœ,œœ+˜£Kš œœœœ(œ˜LKšœ˜—Kš œœœ#œœœ˜jJ˜—šœ˜K˜šžœ˜*Kšœ œA˜TKšœ˜K˜—šžœ˜(šœ œœ2œ˜NKšœœ=˜TKšœ˜K˜—Kšœ˜K˜—šžœ˜0Kšœœx˜‘Kšœ˜—K˜Kšœ˜Kšœ ˜ J˜.Kš œœœœœ˜GKšœœ ˜Kšœ@œœ˜MKšœAœœ˜Nšœœœ˜Kšœœ,˜CKš œœœœœ.œ ˜wKšœmœœ˜zKšœC˜C—Kšœ˜J˜—J˜—J˜šž œ˜&Kšœ œ8˜KKšœ˜K˜—šž œœœ˜7Kšœ˜Kšœœœ?˜bKšœœœœ ˜;Kšœ>˜>Kš œ œœœœ ˜/Kšœ˜K˜—J˜Jš œ œœœœ˜9J˜/Kšœ˜KšœIœ$˜qKš œœœœœ˜GJ˜Jšœœ˜Jšœœ˜J˜$J˜J˜Jšœœ˜Jšœœ˜J˜$J˜šœœœ˜+J˜(Jšœ œœœ˜šœœ˜Jšœ2œœ˜CJšœ2œœ˜CJšœ2œœ˜CJšœ˜—Jšœ˜—Kšœ<œœ˜IJšœœ ˜Kšœ"˜$J˜K˜—šžœ œ(œ˜\KšœBœ˜]Kšœ˜Kšœ˜J˜—š žœœœOœœ$˜“KšœœI˜mJšœ2œœ˜?šœ-œœ˜@šœœ˜Kšœ ˜ Kšœ˜K˜—šœœ˜Kšœœ!˜3KšœY˜YK˜—Kšœ˜—Kšœ˜Kšœ˜J˜—Kšœœ6˜Jšœœ6˜JKšœœœœ˜0šœœœ˜!Kšœ!˜!Kšœœ˜—J˜J˜—J˜——…—AξjJ