<> <> <> <<>> 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[]; < ROPE>> 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]; <<-- name every net (atomic internal)>> FOR i: NAT IN [0..data.size) DO inst: CellInstance _ data.instances[i]; SetName[inst, InstanceName[cellType, inst]]; <<-- name every instance>> WriteCellType[inst.type, s]; <<-- recursively write every sub-cell>> ENDLOOP; PropagateDirection[cellType]; IO.PutF[s, "module %g", IO.rope[Name[cellType]]]; <<-- write header for this non-leaf cell>> 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]; <<-- from recasted to original>> IF IsLeaf[recasted] THEN { <<-- order is important!!!>> 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 <<-- we actually need a VisitAtomic in order!!!>> <<-- and simplify this code, now that the order of the public is correct>> 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; }; <<-- Turns a full wire name into a simpler name: foo[3].bar -> foox3xbar starting with a letter>> 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