<> <> <> <> <<>> 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; < 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 <<'|' 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]; <> 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' 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]; <> 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.