DIRECTORY CDCommandOps, CDSequencer, Core, CoreCDUser, CoreCreate, CoreOps, CoreClasses, CoreProperties, FS, IO, RefTab, Rope, SymTab, TerminalIO; CoreToVImpl: CEDAR PROGRAM IMPORTS CDCommandOps, CoreOps, CoreCDUser, CoreClasses, CoreProperties, FS, IO, RefTab, Rope, SymTab, TerminalIO = BEGIN OPEN CoreCreate; nameTable: RefTab.Ref _ RefTab.Create[]; reverseNameTable: SymTab.Ref _ SymTab.Create[]; hackMenu: ATOM _ $SpecialMenu; -- where DAUserHacks commands are forcefully registered DoWriteCircuit: PROC [command: CDSequencer.Command] = { WriteOne: CoreCDUser.EachRootCellTypeProc ~ { fileName: ROPE _ CoreOps.GetCellTypeName[root]; IF Rope.IsEmpty[fileName] THEN fileName _ "UnnamedCell"; TerminalIO.PutF["Generating Verilog for %g ...\n", IO.rope[fileName]]; fileName _ Rope.Cat[fileName, ".v"]; stream _ FS.StreamOpen[fileName, $create]; WriteCircuit[root, stream]; IO.Close[stream]; TerminalIO.PutF["Verilog: %g generated!\n", IO.rope[fileName]]; quit _ TRUE; -- only one cell }; stream: IO.STREAM; [] _ CoreCDUser.EnumerateSelectedCellTypes[command.design, WriteOne]; }; WriteCircuit: PUBLIC PROC [cellType: CellType, s: IO.STREAM] ~ { Init[]; IO.PutF[s, "// New Circuit\n\n"]; WriteCellType[cellType, s]; }; DefineLeaf: PROC [cellType: CellType] ~ { NameNet: PROC [wire: Wire] ~ { IF HasName[wire] THEN RETURN; SetName[wire, NetName[cellType.public, wire]]; }; name: ROPE = NARROW[CoreProperties.GetCellTypeProp[cellType, vName]]; IF Rope.IsEmpty[name] THEN ERROR; -- a pre-defined cell should have a name! SetName[cellType, name]; IF IsModule[cellType] THEN [] _ CoreOps.VisitRootAtomics[cellType.public, NameNet]; }; WriteCellType: PUBLIC PROC [cellType: CellType, s: IO.STREAM] ~ { NameNet: PROC [wire: Wire] ~ { IF HasName[wire] THEN RETURN; SetName[wire, NetName[data.internal, wire]]; }; data: CoreClasses.RecordCellType; cellType _ CoreOps.ToBasic[cellType]; -- think about this!!! IF HasName[cellType] THEN RETURN; IF IsLeaf[cellType] OR IsModule[cellType] THEN {DefineLeaf[cellType]; RETURN}; IF cellType.class # CoreClasses.recordCellClass THEN ERROR; data _ NARROW[cellType.data]; SetName[cellType, CTName[cellType], TRUE]; -- do this first!!! [] _ CoreOps.VisitRootAtomics[data.internal, NameNet]; FOR i: NAT IN [0..data.size) DO inst: CellInstance _ data.instances[i]; SetName[inst, InstanceName[cellType, inst]]; WriteCellType[inst.type, s]; ENDLOOP; PropagateDirection[cellType]; IO.PutF[s, "module %g", IO.rope[Name[cellType]]]; WritePublic[cellType, s]; WriteInstances[cellType, s]; IO.PutF[s, "endmodule //%g\n\n", IO.rope[Name[cellType]]]; }; WritePublic: PROC [cellType: CellType, s: IO.STREAM] ~ { WritePort: PROC [wire: Wire] ~ { IF IsPower[wire] THEN RETURN; ports _ CONS[Name[wire], ports]; IF GetDir[wire]=$Output THEN outputs _ CONS[Name[wire], outputs] ELSE inputs _ CONS[Name[wire], inputs]; }; ports: LIST OF ROPE _ NIL; inputs: LIST OF ROPE _ NIL; outputs: LIST OF ROPE _ NIL; VisitAtomicsOnce[cellType.public, WritePort]; IO.PutF[s, "("]; PrintList[s, ports]; IO.PutF[s, ");\n"]; IF inputs#NIL THEN IO.PutF[s, "\t input "]; PrintList[s, inputs]; IO.PutF[s, ";\n"]; IF outputs#NIL THEN IO.PutF[s, "\t output "]; PrintList[s, outputs]; IO.PutF[s, ";\n"]; IO.PutF[s, "\t supply0 Gnd; supply1 Vdd; \n"]; }; WriteInstances: PROC [cellType: CellType, s: IO.STREAM] ~ { data: CoreClasses.RecordCellType _ NARROW[cellType.data]; FOR i: NAT IN [0..data.size) DO inst: CellInstance _ data.instances[i]; recasted: CellType _ CoreOps.ToBasic[inst.type]; recastBindings: RefTab.Ref _ CoreOps.CreateBindingTable[recasted.public, inst.type.public]; IF IsLeaf[recasted] THEN { ordered: WireSeq _ recasted.public; IF recasted.public=NIL THEN ERROR; -- every leaf cell should be treated first IO.PutF[s, "\t %g #1 %g(", IO.rope[Name[recasted]], IO.rope[Name[inst]]]; FOR i: NAT IN [0..ordered.size) DO originalPublic: Wire _ NARROW[RefTab.Fetch[recastBindings, ordered[i]].val]; subActual: Wire _ CoreOps.CorrespondingActual[inst.actual, inst.type.public, originalPublic]; IF subActual=NIL OR subActual.size#0 THEN ERROR; IF i>0 THEN IO.PutF[s, ", "]; IO.PutF[s, "%g", IO.rope[Name[subActual]]]; ENDLOOP; IO.PutF[s, ");\n"]; } ELSE { WriteBinding: PROC [actualWire, publicWire: Wire] ~ { bd: ROPE; IF IsPower[publicWire] AND IsPower[actualWire] THEN RETURN; bd _ Rope.Cat[".", Name[publicWire], "(", Name[actualWire], ")"]; bindings _ CONS[bd, bindings]; }; bindings: LIST OF ROPE _ NIL; IO.PutF[s, "\t %g %g(", IO.rope[Name[recasted]], IO.rope[Name[inst]]]; [] _ CoreOps.VisitAtomicPairs[inst.actual, recasted.public, WriteBinding]; PrintList[s, bindings]; IO.PutF[s, ");\n"]; }; ENDLOOP; }; vTypeProp: ATOM = $VerilogType; vName: ATOM = $VerilogName; dirProp: ATOM = $VerilogDirection; nbGatesProp: ATOM = $LSIGates; maxLineLength: INT = 200; PrintList: PROC [s: IO.STREAM, list: LIST OF ROPE] ~ { start: INT _ IO.GetIndex[s]; IF list=NIL THEN RETURN; IO.PutF[s, "%g", IO.rope[list.first]]; FOR l: LIST OF ROPE _ list.rest, l.rest WHILE l#NIL DO IF (IO.GetIndex[s]-start)>maxLineLength THEN {start _ IO.GetIndex[s]; IO.PutF[s, ",\n"]} ELSE IO.PutF[s, ", "]; IO.PutF[s, "%g", IO.rope[l.first]]; ENDLOOP; }; IsPower: PROC [wire: Wire] RETURNS [BOOL] ~ { RETURN [Rope.Equal[Name[wire], "Vdd"] OR Rope.Equal[Name[wire], "Gnd"]]; }; GetDir: PROC [wire: Wire] RETURNS [dir: ATOM] ~ { RETURN [NARROW[CoreProperties.GetWireProp[wire, dirProp]]]; }; IsLeaf: PROC [cellType: CellType] RETURNS [BOOL] ~ { RETURN [CoreProperties.GetCellTypeProp[cellType, vTypeProp]=$Leaf]; }; IsModule: PROC [cellType: CellType] RETURNS [BOOL] ~ { RETURN [CoreProperties.GetCellTypeProp[cellType, vTypeProp]=$Module]; }; VisitAtomicsOnce : PUBLIC PROC [root: WireSeq, eachWire: PROC [Wire]] = { VisitAtomicWires: PROC [wire: Wire] = { IF wire.size=0 THEN { IF RefTab.Fetch[alreadySeen, wire].found THEN RETURN ELSE {eachWire[wire]; [] _ RefTab.Store[alreadySeen, wire, $Yep];} } ELSE FOR i: NAT IN [0 .. wire.size) DO VisitAtomicWires[wire[i]] ENDLOOP; }; alreadySeen: RefTab.Ref _ RefTab.Create[]; FOR i: NAT IN [0 .. root.size) DO VisitAtomicWires[root[i]] ENDLOOP; }; PropagateDirection: PROC [cellType: CellType] ~ { UpdateDir: PROC [actualWire, publicWire: Wire] ~ { dir, pubDir: ATOM; dir _ GetDir[actualWire]; pubDir _ GetDir[publicWire]; IF dir=NIL THEN dir _ $Input; IF dir=$Output OR pubDir=$Output THEN CoreProperties.PutWireProp[actualWire, dirProp, $Output] ELSE CoreProperties.PutWireProp[actualWire, dirProp, $Input]; }; data: CoreClasses.RecordCellType _ NARROW[cellType.data]; FOR i: NAT IN [0..data.size) DO inst: CellInstance _ data.instances[i]; recasted: CellType _ CoreOps.ToBasic[inst.type]; recastBindings: RefTab.Ref _ CoreOps.CreateBindingTable[recasted.public, inst.type.public]; CoreOps.VisitAtomicPairs[inst.actual, recasted.public, UpdateDir]; ENDLOOP; }; nameMagicCounter: NAT _ 0; NewName: PROC [] RETURNS [name: ROPE] ~ { -- Generates a new name on every call nameMagicCounter _ nameMagicCounter+1; name _ IO.PutFR["Cell%g", IO.int[nameMagicCounter]]; }; HasName: PROC [ref: REF] RETURNS [BOOL] ~ { RETURN [RefTab.Fetch[nameTable, ref].found]; }; Name: PROC [ref: REF] RETURNS [ROPE] ~ { found: BOOL; val: REF; [found, val] _ RefTab.Fetch[nameTable, ref]; IF ~found THEN ERROR; RETURN [NARROW[val]]; }; SetName: PROC [ref: REF, name: ROPE, unique: BOOL _ FALSE] ~ { found: BOOL; val: REF; [found, val] _ RefTab.Fetch[nameTable, ref]; IF found THEN ERROR; -- we should never encounter twice the same ref IF unique THEN { IF SymTab.Fetch[reverseNameTable, name].found THEN name _ NewName[]; [] _ SymTab.Store[reverseNameTable, name, ref]; }; [] _ RefTab.Store[nameTable, ref, name]; }; IsLetter: PROC [c: CHAR] RETURNS [BOOL] ~ {RETURN [(c IN ['A..'Z]) OR (c IN ['a..'z])]}; IsDigit: PROC [c: CHAR] RETURNS [BOOL] ~ {RETURN [(c IN ['0..'9])]}; Sanitize: PROC [old: CHAR] RETURNS [new: CHAR] ~ { -- Replaces funny characters by x's new _ IF IsLetter[old] OR IsDigit[old] THEN old ELSE 'x; }; CanonizeWireName: PROC [name: ROPE] RETURNS [simpler: ROPE] ~ { components: LIST OF ROPE; [simpler, components] _ CoreOps.ParseWireName[name]; FOR l: LIST OF ROPE _ components, l.rest WHILE l#NIL DO simpler _ Rope.Cat[simpler, "x", l.first]; ENDLOOP; IF Rope.IsEmpty[name] THEN RETURN[NewName[]]; simpler _ Rope.Translate[base: name, translator: Sanitize]; IF IsDigit[Rope.Fetch[simpler, 0]] THEN simpler _ Rope.Cat["x", simpler]; }; SanitizeName: PROC [name: ROPE] RETURNS [simpler: ROPE] ~ { IF Rope.IsEmpty[name] THEN RETURN[NewName[]]; simpler _ Rope.Translate[base: name, translator: Sanitize]; IF IsDigit[Rope.Fetch[simpler, 0]] THEN simpler _ Rope.Cat["x", simpler]; }; NetName: PROC [root: WireSeq, wire: Wire] RETURNS [name: ROPE] ~ { fullName: ROPE _ CoreOps.GetFullWireName[root, wire]; IF Rope.IsEmpty[fullName] THEN ERROR; name _ CanonizeWireName[fullName]; }; CTName: PROC [cellType: CellType] RETURNS [name: ROPE] ~ { RETURN [SanitizeName[CoreOps.GetCellTypeName[cellType]]]; }; InstanceName: PROC [parent: CellType, instance: CellInstance] RETURNS [name: ROPE] ~ { index: INT; name _ CoreClasses.GetCellInstanceName[instance]; IF ~Rope.IsEmpty[name] THEN RETURN[SanitizeName[name]]; index _ CoreClasses.InstanceIndex[parent, instance]; IF index=-1 THEN ERROR; name _ IO.PutFR["Inst%g", IO.int[index]]; }; SizeRef: TYPE = REF SizeRec; SizeRec: TYPE = RECORD[totalArea: REAL _ 0.0, nb: INT _ 0]; GetAreaProc: TYPE ~ PROC [cell: CellType] RETURNS [area: REAL, key: ROPE]; Leaf: GetAreaProc ~ { val: REF _ CoreProperties.GetCellTypeProp[cell, nbGatesProp]; intVal: INT _ IF val=NIL THEN 0 ELSE NARROW[val, REF INT]^; area _ REAL[intVal]; -- square microns key _ CoreOps.GetCellTypeName[cell]; }; HistogramProp: PROC [cell: CellType, proc: GetAreaProc, table: SymTab.Ref] RETURNS [area: REAL _ 0.0, nbCells: INT _ 0] ~ { key: ROPE; [area, key] _ proc[cell]; -- total area IF area#0 THEN { ref: REF; item: SizeRef; found: BOOL; [found, ref] _ SymTab.Fetch[table, key]; IF found THEN { item _ NARROW[ref]; item.totalArea _ item.totalArea+area; item.nb _ item.nb+1} ELSE item _ NEW[SizeRec _ [totalArea: area, nb: 1]]; [] _ SymTab.Store[table, key, item]; RETURN[area, 1]; }; SELECT TRUE FROM cell.class=CoreClasses.recordCellClass => { rct: CoreClasses.RecordCellType _ NARROW[cell.data]; FOR i: NAT IN [0..rct.size) DO subSize: REAL _ 0.0; subNb: INT _ 0; [subSize, subNb] _ HistogramProp[rct[i].type, proc, table]; area _ area+subSize; nbCells _ nbCells+subNb; ENDLOOP; }; cell.class.recast=NIL => RETURN[0, 0]; -- all other atomic classes are ignored ENDCASE => RETURN HistogramProp[CoreOps.Recast[cell], proc, table]; -- recast }; SortAndPrint: PROC [table: SymTab.Ref, area: REAL] ~ { WHILE SymTab.GetSize[table]>0 DO -- the laziest way to sort a few entries largest: ROPE; value: REF; maxSize: REAL _ 0.0; SelectLargestItem: SymTab.EachPairAction ~ { item: SizeRef _ NARROW[val]; name: ROPE _ NARROW[key]; IF item.totalArea > maxSize THEN {largest _ key; maxSize _ item.totalArea}; }; PrintItem: PROC [name: ROPE, item: SizeRef] ~ { TerminalIO.PutF["%g %g => %5.0f gates, %5.1f%% of total area.\n", IO.int[item.nb], IO.rope[name], IO.real[item.totalArea], IO.real[100*item.totalArea/area]]; }; [] _ SymTab.Pairs[table, SelectLargestItem]; value _ SymTab.Fetch[table, largest].val; PrintItem[NARROW[largest], NARROW[value]]; [] _ SymTab.Delete[table, largest]; ENDLOOP; }; Measure: CoreCDUser.EachRootCellTypeProc ~ { leafTable: SymTab.Ref _ SymTab.Create[]; -- "xnor2" -> [totalArea, nb] area: REAL _ 0.0; nbCells: INT _ 0; TerminalIO.PutF["\nEstimating gate count of %g in LSI Logic equivalent gates\n", IO.rope[CoreOps.GetCellTypeName[root]]]; [area, nbCells] _ HistogramProp[root, Leaf, leafTable]; TerminalIO.PutF["The cell has %g primitives, and %g gates\n", IO.int[nbCells], IO.real[area]]; SortAndPrint[leafTable, area]; }; ExtractAndMeasure: PROC [comm: CDSequencer.Command] = { [] _ CoreCDUser.EnumerateSelectedCellTypes[comm.design, Measure]; }; Init: PROC ~ { RefTab.Erase[nameTable]; SymTab.Erase[reverseNameTable]; }; Init[]; CDCommandOps.RegisterWithMenu[hackMenu, "Generate Verilog", "Generate file .v", $GenerateVerilog, DoWriteCircuit]; CDCommandOps.RegisterWithMenu[hackMenu, "Number of gates", "Estimate number of LSILogic gates", $ExtractAndMeasure, ExtractAndMeasure]; END. _ &ct _ Logic.Nand[7] _ &ct _ Logic.Nand[17] _ CoreToVImpl.WriteCircuit[&ct, TerminalIO.TOS[]] _ CoreOps.Print[&ct] (Q, QN)= FD1 (D, CP) (Q, QN)= LD1 (D, G) Z= BTS5 (A, E) -- inverting; A is the data Gate counts: FD1 7 LD1 5 BTS5 3 Install CellLibraries; run -a LogicVSimpleImpl CoreToVImpl rCoreToVImpl.mesa Copyright Σ 1987 by Xerox Corporation. All rights reserved. written by Ch. Louis Monier October 17, 1988 9:51:34 am PDT cellType, instance, or atomic internal -> ROPE Public Procs -- name every net (atomic internal) -- name every instance -- recursively write every sub-cell -- write header for this non-leaf cell -- from recasted to original -- order is important!!! -- we actually need a VisitAtomic in order!!! -- and simplify this code, now that the order of the public is correct Utilities Names -- Turns a full wire name into a simpler name: foo[3].bar -> foox3xbar starting with a letter Init ΚΖ˜codešœ™K™šœ6˜6KšŸ#™#—šœœœ˜Kšœ'˜'šœ,˜,KšŸ™—šœ˜KšŸ#™#—Kšœ˜—Kšœ˜šœœ˜1KšŸ&™&—Kšœ˜Kšœ˜Kšœœ˜:Kšœ˜K˜—š  œœœœ˜8š  œœ˜ Kšœœœ˜Kšœœ˜ Kšœœ œ˜AKšœ œ˜'K˜—Kš œœœœœ˜Kš œœœœœ˜Kš œ œœœœ˜Kšœ-˜-Kšœ$œ˜9Kš œœœœ-œ˜TKš œ œœœ/œ˜WKšœ,˜.Kšœ˜K˜—š œœœœ˜;Kšœ#œ˜9šœœœ˜Kšœ'˜'Kšœ0˜0šœ[˜[KšŸ™—šœœ˜KšΠbc™Kšœ#˜#Kš œœœœŸ*˜MKšœœœ˜Išœœœ˜"KšΟb-™-Kš’F™FKšœœ/˜LKšœ]˜]Kš œ œœœœ˜0Kšœœœ˜Kšœœ˜+Kšœ˜—Kšœ˜Kšœ˜—šœ˜š  œœ#˜5Kšœœ˜ Kšœœœœ˜;KšœA˜AKšœ œ˜K˜—Kš œ œœœœ˜Kšœœœ˜FKšœJ˜JKšœ˜Kšœ˜K˜—Kšœ˜—Kšœ˜——™ Kšœ œ˜Kšœœ˜Kšœ œ˜"Kšœ œ ˜Kšœœ˜š  œœœœœœœ˜6Kšœœœ ˜Kšœœœœ˜Kšœœ˜&š œœœœœœ˜6Kš œœ"œ œœ˜XKšœœ˜Kšœœ˜#Kšœ˜—K˜—š œœœœ˜-Kšœ œ ˜HK˜—š œœœœ˜1Kšœœ-˜;K˜—š œœœœ˜4Kšœ=˜CK˜—š œœœœ˜6Kšœ?˜EK˜—š œœœœ ˜Iš œœ˜'šœ œ˜Kšœ'œ˜4Jšœ>˜BJ˜—Kš œœœœœœ˜IJšœ˜—Jšœ*˜*Jš œœœœœ˜DJšœ˜—š œœ˜1š  œœ#˜2Kšœ œ˜Kšœ˜Kšœ˜Kšœœœœ˜šœ œœ˜&Kšœ8˜8—Kšœ9˜=K˜—Kšœ#œ˜9šœœœ˜Kšœ'˜'Kšœ0˜0Kšœ[˜[KšœB˜BKšœ˜—K˜——™Kšœœ˜š  œœœœŸ%˜OKšœ&˜&Kšœœœ˜4K˜—š  œœœœœ˜+Kšœ&˜,K˜—š  œœœœœ˜(Kšœœœ˜Kšœ,˜,Kšœœœ˜Kšœœ˜K˜—š  œœœœ œœ˜>Kšœœœ˜Kšœ,˜,KšœœœŸ/˜Dšœœ˜Kšœ,œ˜DKšœ/˜/K˜—Kšœ(˜(K˜—Kš œœœœœœœ œœ ˜XKš œœœœœœœ ˜Dš  œœœœœŸ#˜VKš œœœœœ˜8K˜—KšŸ]™]š  œœœœ œ˜?Kšœ œœœ˜Kšœ4˜4š œœœœœœ˜7Kšœ*˜*Kšœ˜—Kšœœœ ˜-Kšœ;˜;Kšœ!œ"˜IK˜—š   œœœœ œ˜;Kšœœœ ˜-Kšœ;˜;Kšœ!œ"˜IK˜—K˜š œœœœ˜BKšœ œ'˜5Kšœœœ˜%Kšœ"˜"K˜—š œœœœ˜:Kšœ3˜9K˜—š  œœ,œœ˜VKšœœ˜ Kšœ1˜1Kšœœœ˜7Kšœ4˜4Kšœ œœ˜Kšœœœ ˜)K˜—K˜Kšœ œœ ˜Kš œ œœ œ œ˜;Kš œ œœœœœ˜Jš œ˜Kšœœ5˜=Kšœœœœœœœœœ˜;Kšœœ Ÿ˜&Jšœ$˜$K˜K˜—š   œœ8œœœ ˜{Kšœœ˜ KšœŸ˜(šœœ˜Kšœœ˜ Kšœ˜Kšœœ˜ Kšœ(˜(šœœ˜Kšœœ˜Kšœ%˜%Kšœ˜—Kšœœ%˜4Kšœ$˜$Kšœ ˜Kšœ˜—šœœ˜šœ+˜+Kšœ"œ ˜4šœœœ˜Kšœ œ˜Kšœœ˜Kšœ;˜;Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—KšœœœŸ'˜NKšœœ4Ÿ ˜N—K˜K˜—š  œœœ˜6šœœŸ(˜IJšœ œ˜Jšœœ˜ Jšœ œ˜š œ˜,Jšœœ˜Jšœœœ˜Kšœœ+˜KK˜—š  œœœ˜/Jš œBœœ œœ ˜K˜—Jšœ,˜,Jšœ)˜)Jšœ œ œ ˜*Jšœ#˜#Kšœ˜—K˜K˜—š œ%˜,Jšœ)Ÿ˜FJšœœ˜Jšœ œ˜JšœQœ&˜yJšœ7˜7Jšœ>œœ ˜^Jšœ˜K˜—š œœ ˜7JšœA˜AJ˜—K˜—™š œœ˜Kšœ˜Kšœ˜K˜—J˜K˜Kšœx˜xKšœ‡˜‡K˜K˜—Kšœ˜K˜Kšœ˜Kšœ˜Kšœ+œ˜1K˜Kšœ˜K˜Kš’˜K˜K˜K˜K˜K˜+K˜˜ K˜K˜K˜K˜—K˜K˜Kšœ:˜:K˜—…—1CN